#!/bin/bash

set -u

L4T_VER=
L4T_VER_MAJOR=
JETPACK_VER=
BOARD_NAME=
JETSON_MODULE=

L4T_TOP=
L4T_ST_FILE=
L4T_ST_KEY_VER="L4T"
L4T_ST_KEY_BOARD="BOARD"
L4T_ST_KEY_KERNEL_DTB="KERNEL"
L4T_ST_KEY_STATUS="STATUS"
L4T_ST_KEY_OPT="OPTION"
L4T_ST_KEY_PREV="PREV"
PASSWD=123
L4T_BSP_FIX_CMD="return"

L4T_CHANGED_TOP=
L4T_PATCH_TOP=
L4T_RELEASE_PKG=

L4T_VERMAGIC=
L4T_SRC_TOP=
L4T_SRC_MAKE_TOP=
L4T_SRC_OUTPUT_TOP=

# Define DTB for OEM Board
_L4T_DTB_XAVIER_NX="tegra194-p3668-all-p3509-0000.dtb"
_L4T_DTB_AGX_XAVIER="tegra194-p2888-0001-p2822-0000.dtb"
_L4T_DTB_AGX_XAVIER_INDUSTRIAL="tegra194-p2888-0008-p2822-0000.dtb"
_L4T_DTB_ORIN_NX="tegra234-p3767-0000-p3509-a02.dtb"
_L4T_DTB_AGX_ORIN="tegra234-p3701-0000-p3737-0000.dtb"
_L4T_DTB_AGX_ORIN_32GB="tegra234-p3701-0004-p3737-0000.dtb"
_L4T_DTB_AGX_ORIN_64GB=${_L4T_DTB_AGX_ORIN}
declare -A L4T_DTB_ARRAY
L4T_DTB_ARRAY["DEVKIT-XAVIER-NX"]=${_L4T_DTB_XAVIER_NX}
L4T_DTB_ARRAY["DEVKIT-AGX-XAVIER"]=${_L4T_DTB_AGX_XAVIER}
L4T_DTB_ARRAY["DEVKIT-AGX-XAVIER-I"]=${_L4T_DTB_AGX_XAVIER_INDUSTRIAL}
L4T_DTB_ARRAY["DEVKIT-AGX-ORIN"]=${_L4T_DTB_AGX_ORIN}
L4T_DTB_ARRAY["SHENZHU"]=${_L4T_DTB_AGX_XAVIER}
L4T_DTB_ARRAY["RKNS02NX0"]=${_L4T_DTB_XAVIER_NX}
L4T_DTB_ARRAY["RKNS02N1"]="ritrontek_p3668-0003_rkns02n1-v1.dtb"
L4T_DTB_ARRAY["RKNB10XA2"]=${_L4T_DTB_AGX_XAVIER}
L4T_DTB_ARRAY["RKPE32AN0"]=${_L4T_DTB_AGX_XAVIER_INDUSTRIAL}
L4T_DTB_ARRAY["RKPE300A2"]=${_L4T_DTB_AGX_XAVIER}
L4T_DTB_ARRAY["RKNS0AN0-32"]="ritrontek_p3701-0004_rkns0an0-v01.dtb"
L4T_DTB_ARRAY["RKNS0AN0-64"]=${_L4T_DTB_AGX_ORIN_64GB}
L4T_DTB_ARRAY["RKCR3NX0"]=${_L4T_DTB_ORIN_NX}
L4T_DTB_ARRAY["RKPE3NX1"]="ritrontek_p3767-0000_rkpe3nx1-v01.dtb"
L4T_DTB_ARRAY["RK513Z02"]="ritrontek_p3701-0004_rk513z02-v01.dtb"

# Define CONF for OEM Board
_L4T_CONF_XAVIER_NX="p3509-0000+p3668-0001-qspi-emmc"
_L4T_CONF_AGX_XAVIER="p2822-0000+p2888-0004"
_L4T_CONF_AGX_XAVIER_INDUSTRIAL="p2822-0000+p2888-0008"
_L4T_CONF_ORIN_NX="p3509-a02+p3767-0000"
_L4T_CONF_AGX_ORIN="p3737-0000+p3701-0000"
declare -A L4T_CONF_ARRAY
L4T_CONF_ARRAY["DEVKIT-XAVIER-NX"]=${_L4T_CONF_XAVIER_NX}
L4T_CONF_ARRAY["DEVKIT-AGX-XAVIER"]=${_L4T_CONF_AGX_XAVIER}
L4T_CONF_ARRAY["DEVKIT-AGX-XAVIER-I"]=${_L4T_CONF_AGX_XAVIER_INDUSTRIAL}
L4T_CONF_ARRAY["DEVKIT-AGX-ORIN"]=${_L4T_CONF_AGX_ORIN}
L4T_CONF_ARRAY["SHENZHU"]=${_L4T_CONF_AGX_XAVIER}
L4T_CONF_ARRAY["RKNS02NX0"]=${_L4T_CONF_XAVIER_NX}
L4T_CONF_ARRAY["RKNS02N1"]="ritrontek_p3668-0003_rkns02n1-v1"
L4T_CONF_ARRAY["RKNB10XA2"]=${_L4T_CONF_AGX_XAVIER}
L4T_CONF_ARRAY["RKPE32AN0"]=${_L4T_CONF_AGX_XAVIER_INDUSTRIAL}
L4T_CONF_ARRAY["RKPE300A2"]=${_L4T_CONF_AGX_XAVIER}
L4T_CONF_ARRAY["RKCR3NX0"]=${_L4T_CONF_ORIN_NX}
L4T_CONF_ARRAY["RKPE3NX1"]="ritrontek_p3767-0000_rkpe3nx1-v01"
L4T_CONF_ARRAY["RKNS0AN0-32"]="ritrontek_p3701-0004_rkns0an0-v01"
L4T_CONF_ARRAY["RKNS0AN0-64"]=${_L4T_CONF_AGX_ORIN}
L4T_CONF_ARRAY["RK513Z02"]="ritrontek_p3701-0004_rk513z02-v01"

# Define L4T SRC Branch for OEM Board
_L4T_SRC_BRANCH_DEVKIT="master"
_L4T_SRC_BRANCH_XAVIER_NX_AUDIO_RKNS02280="xavier_nx_rk_ns02nx0"
_L4T_SRC_BRANCH_AGX_XAVIER_GMSL2_SHENZHU="xavier_gmsl2_shenzhu_board"
_L4T_SRC_BRANCH_AGX_XAVIER_GMSL2_CTIJCB002="xavier_gmsl2_cti_jcb002"
_L4T_SRC_BRANCH_AGX_XAVIER_GMSL2_RKNS02280="xavier_gmsl2_rk_ns02280"
_L4T_SRC_BRANCH_AGX_XAVIER_GMSL2_RKNS02240="xavier_gmsl2_rk_ns02240"
_L4T_SRC_BRANCH_AGX_XAVIER_RKPE300A2="xavier_rk_pe300a2"
_L4T_SRC_BRANCH_ORIN_NX_RKCR3NX0="orin_nx_rkcr3nx0"
#_L4T_SRC_BRANCH_AGX_ORIN_RKNS0AN0="orin_rkns0an0"
_L4T_SRC_BRANCH_AGX_ORIN_RKNS0AN0="orin_rkns0an0_rkcam"
declare -A L4T_SRC_BRANCH_ARRAY
L4T_SRC_BRANCH_ARRAY["DEVKIT-XAVIER-NX"]=${_L4T_SRC_BRANCH_DEVKIT}
L4T_SRC_BRANCH_ARRAY["DEVKIT-AGX-XAVIER"]=${_L4T_SRC_BRANCH_DEVKIT}
L4T_SRC_BRANCH_ARRAY["DEVKIT-AGX-XAVIER-I"]=${_L4T_SRC_BRANCH_DEVKIT}
L4T_SRC_BRANCH_ARRAY["DEVKIT-AGX-ORIN"]=${_L4T_SRC_BRANCH_DEVKIT}
L4T_SRC_BRANCH_ARRAY["SHENZHU"]=${_L4T_SRC_BRANCH_AGX_XAVIER_GMSL2_SHENZHU}
L4T_SRC_BRANCH_ARRAY["RKNS02NX0"]=${_L4T_SRC_BRANCH_XAVIER_NX_AUDIO_RKNS02280}
L4T_SRC_BRANCH_ARRAY["RKNS02N1"]=${_L4T_SRC_BRANCH_DEVKIT}
L4T_SRC_BRANCH_ARRAY["RKNB10XA2"]=${_L4T_SRC_BRANCH_AGX_XAVIER_GMSL2_CTIJCB002}
L4T_SRC_BRANCH_ARRAY["RKPE32AN0"]=${_L4T_SRC_BRANCH_AGX_XAVIER_GMSL2_RKNS02240}
L4T_SRC_BRANCH_ARRAY["RKPE300A2"]=${_L4T_SRC_BRANCH_AGX_XAVIER_RKPE300A2}
L4T_SRC_BRANCH_ARRAY["RKCR3NX0"]=${_L4T_SRC_BRANCH_ORIN_NX_RKCR3NX0}
L4T_SRC_BRANCH_ARRAY["RKPE3NX1"]=${_L4T_SRC_BRANCH_DEVKIT}
L4T_SRC_BRANCH_ARRAY["RKNS0AN0-32"]=${_L4T_SRC_BRANCH_DEVKIT}
L4T_SRC_BRANCH_ARRAY["RKNS0AN0-64"]=${_L4T_SRC_BRANCH_AGX_ORIN_RKNS0AN0}
L4T_SRC_BRANCH_ARRAY["RK513Z02"]=${_L4T_SRC_BRANCH_DEVKIT}

# Kernel Build Toolchain
L4T_TOOLCHAIN_TOP="${HOME}/nvidia/toolchain"
declare -A L4T_TOOLCHAIN_ARRAY
_L4T_TOOLCHAIN_0="/usr/bin/aarch64-linux-gnu-"
_L4T_TOOLCHAIN_1="${L4T_TOOLCHAIN_TOP}/Toolchain_Linaro_GCC_7.3.1/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-"
_L4T_TOOLCHAIN_2="${L4T_TOOLCHAIN_TOP}/Toolchain_Bootlin_GCC_9.3/aarch64--glibc--stable-final/bin/aarch64-linux-"
L4T_TOOLCHAIN_ARRAY["32.5.0"]=${_L4T_TOOLCHAIN_0}
L4T_TOOLCHAIN_ARRAY["32.7.1"]=${_L4T_TOOLCHAIN_0}
L4T_TOOLCHAIN_ARRAY["35.1.0"]=${_L4T_TOOLCHAIN_2}
L4T_TOOLCHAIN_ARRAY["35.3.1"]=${_L4T_TOOLCHAIN_2}

CMD_BSP="bsp"
CMD_BSP_ST="st"
CMD_BSP_RELEASE="release"
CMD_BSP_INSTALL="install"
CMD_BSP_UNINSTALL="uninstall"
CMD_BSP_BACKUP="backup"
CMD_BSP_RESTORE="restore"

CMD_SRC="src"
CMD_SRC_BUILD="build"
CMD_SRC_BUILD_KERNEL="kernel"
CMD_SRC_BUILD_DTBS="dtbs"
CMD_SRC_BUILD_MODULES="modules"
CMD_SRC_INSTALL="install"

CMD_PATCH="patch"
CMD_PATCH_GEN="gen"
CMD_PATCH_APPLY="apply"
CMD_PATCH_REVERT="revert"

CMD_SWITCH="switch"
CMD_SWITCH_BSP="bsp"
CMD_SWITCH_SRC="src"

CMD_FLASH="flash"
CMD_FLASH_EMMC="emmc"
CMD_FLASH_NVME="nvme"

function usage()
{
	echo -e "\nUsage: $0 <BSPVer> <BoardName> <CMD> [opts]"
	echo -e "Jetson BSP Development tool\n"
	echo -e "  BSPVer can be one of:"
	echo -e "    4.5   / 32.5.0 --- JetPack4.5, L4T32.5.0"
	echo -e "    4.6.1 / 32.7.1 --- JetPack4.6.1, L4T32.7.1"
	echo -e "    5.0.2 / 35.1.0 --- JetPack5.0.2, L4T35.1.0"
	echo -e "    5.1.1 / 35.3.1 --- JetPack5.1.1, L4T35.3.1"
	echo -e "  BoardName canbe one of the NVIDIA Jetson DEVKIT or Ritrontek Board List:"
	echo -e "    DEVKIT-XAVIER-NX"
	echo -e "    DEVKIT-AGX-XAVIER"
	echo -e "    DEVKIT-AGX-XAVIER-I"
	echo -e "    DEVKIT-AGX-ORIN"
	echo -e "    SHENZHU ----------- [AGX Xavier] Devkit + Shenzhu Camera Board"
	echo -e "    RKNS02NX0 --------- [Xavier NX] NX Mini Box"
	echo -e "    RKNS02N1  --------- [Xavier NX] NX Mini Box2 (add GMSL)"
	echo -e "    RKNB10XA2 --------- [AGX Xavier] AI Machine"
	echo -e "    RKPE32AN0 --------- [AGX Xavier] CIDI Machine"
	echo -e "    RKPE300A2 --------- [AGX Xavier] WangShen Designed Base Board"
	echo -e "    RKCR3NX0 ---------- [Orin NX] GPU Board"
	echo -e "    RKPE3NX1 ---------- [Orin NX] HuaXing (USB Ethernet)"
	echo -e "    RKNS0AN0-32/64 ---- [AGX Orin 32GB/64GB] AI Machine Small"
	echo -e "    RK513Z02 ---------- [AGX Orin 32GB] JunHang Phytium D2000 + NVIDIA AGX Orin"
	echo -e "  CMD can be one of the below:"
	echo -e "    bsp <opt> ------ L4T BSP function"
	echo -e "       st ------------- Get current BSP status"
	echo -e "       release -------- Release L4T BSP into a package"
	echo -e "       install <pkg> -- Install the package into L4T"
	echo -e "       uninstall ------ Unistall the package from L4T"
	echo -e "       backup --------- Backup the images from Device to package"
	echo -e "       restore <pkg> -- Restore the package to Device"
	echo -e "    flash [<device> [opt]] -- L4T Flash function, default to flash whole BSP to mmcblk0p1"
	echo -e "       <device> can be one of the below:"
	echo -e "          emmc -------- Flash to Jetson emmc"
	echo -e "          nvme -------- Flash to Jetson to setup an external nvme drive"
	echo -e "       [opts] --------- Flash options directly pass to flash.sh"
	echo -e "    patch <opt> ---- BSP Patch function"
	echo -e "       gen ------------ Generate the patch files"
	echo -e "       apply ---------- Apply patch for Jetpack BSP"
	echo -e "       revert --------- Revert patch for JetPack BSP"
	echo -e "    switch <opt> --- Switch BSP Kernel and DTB, it support following opts"
	echo -e "       bsp ------------ Switch to BSP original bin"
	echo -e "       src ------------ Switch to Kernel source Image and DTB"
	echo -e "    src <opt> ------ L4T SRC Code function"
	echo -e "       build [opt] ---- Build L4T SRC Kernel and DTB BSP"
	echo -e "          kernel --------- Build L4T Kernel Image"
	echo -e "          dtbs ----------- Build L4T Kernel DTBs"
	echo -e "          modules -------- Build L4T Kernel Modules"
	echo -e "       install -------- Install L4T Kernel SRC Image & DTB & Modules\n"
}

function init_host_env()
{
	# NVIDIA Official Host Init script:
	# -> ${L4T_TOP}/tools/l4t_flash_prerequisites.sh

	# Check needed package whether installed
	type dtc &> /dev/null
	ret=$?
	if [[ ${ret} -ne 0 ]]; then
		echo -e "[INFO] Install needed packages:"
		sudo_cmd "apt update && apt install -y python python3 \
					qemu-user-static \
					device-tree-compiler \
					pkg-config \
					libxml2-utils \
					simg2img \
					network-manager \
					abootimg \
					sshpass \
					flex \
					bison \
					libssl-dev \
					libncurses-dev \
					nfs-kernel-server"
		ret=$?
		if [[ ${ret} -ne 0 ]]; then
			echo -e "[ERROR] Install packages failed\n"
			exit 1
		fi
	fi

	# Check dtc version >=1.5.0, upgrade it if needed
	local dtc_ver=$(dtc -v | awk '{print $3}' | sed 's/\.//g' 2>/dev/null)
	ret=$?
	if [[ ${ret} -ne 0 || ${dtc_ver} -lt 150 ]]; then
		echo -e "[WARN] \"dtc\" version is lower than 1.5.0, now install it\n"

		libftd_pkg="libfdt1_1.5.1-1_amd64.deb"
		dtc_pkg="device-tree-compiler_1.5.1-1_amd64.deb"

		if [[ ! -f ${libftd_pkg} ]]; then
			wget https://mirrors.aliyun.com/ubuntu/pool/main/d/device-tree-compiler/libfdt1_1.5.1-1_amd64.deb
		fi
		if [[ ! -f ${dtc_pkg} ]]; then
			wget https://mirrors.aliyun.com/ubuntu/pool/main/d/device-tree-compiler/device-tree-compiler_1.5.1-1_amd64.deb
		fi

		sudo_cmd "apt install -y ./${libftd_pkg} ./${dtc_pkg}"
		ret=$?
		if [[ ${ret} -ne 0 ]]; then
			echo -e "[ERROR] Download and install \"device-tree-compiler 1.5.1\" error:"
			echo -e ">>> ${libftd_pkg}"
			echo -e ">>> ${dtc_pkg}\n"
			exit 1
		fi
		rm ${libftd_pkg} ${dtc_pkg}

		dtc_ver=$(dtc -v | awk '{print $3}' | sed 's/\.//g' 2>/dev/null)
		ret=$?
		if [[ ${ret} -ne 0 || ${dtc_ver} -lt 150 ]]; then
			echo -e "[ERROR] \"dtc\" version cannot lower than 1.5.0.\n"
			exit 1
		fi
	fi

	# Check udisk2.service whether stopped
	local service_status=$(systemctl is-active udisks2.service)
	if [[ ${service_status} == "active" ]]; then
		sudo_cmd "systemctl stop udisks2.service"
	fi
}

function init_global_var()
{
	local BSP_VER=$1
	BOARD_NAME=$2

	case ${BOARD_NAME} in
		"DEVKIT-XAVIER-NX" | "RKNS02NX0" | "RKNS02N1" )
			JETSON_MODULE="JETSON_XAVIER_NX"
			;;
		"DEVKIT-AGX-XAVIER" | "SHENZHU" | "RKNB10XA2" | "RKPE300A2")
			JETSON_MODULE="JETSON_AGX_XAVIER"
			;;
		"DEVKIT-AGX-XAVIER-I" | "RKPE32AN0")
			JETSON_MODULE="JETSON_AGX_XAVIER Industrial"
			;;
		"RKCR3NX0" | "RKPE3NX1")
			JETSON_MODULE="JETSON_ORIN_NX"
			;;
		"DEVKIT-AGX-ORIN" | "RKNS0AN0-32" | "RK513Z02")
			JETSON_MODULE="JETSON_AGX_ORIN 32GB"
			;;
		"RKNS0AN0-64")
			JETSON_MODULE="JETSON_AGX_ORIN 64GB"
			;;
		*)
			echo -e "[ERROR] Unsupport Board Name: ${BOARD_NAME}\n"
			usage
			exit 1
			;;
	esac

	local JETSON_MODULE_SOC=$(echo ${JETSON_MODULE} | awk '{print $1}')
	case ${BSP_VER} in
		"4.5" | "32.5.0")
			JETPACK_VER=4.5
			L4T_VER=32.5.0
			;;
		"4.6.1" | "32.7.1")
			JETPACK_VER=4.6.1
			L4T_VER=32.7.1
			;;
		"5.0.2" | "35.1.0")
			JETPACK_VER=5.0.2
			L4T_VER=35.1.0
			;;
		"5.1.1" | "35.3.1")
			JETPACK_VER=5.1.1
			L4T_VER=35.3.1
			;;
		*)
			echo -e "[ERROR] Unsupport BSP Version: ${BSP_VER}\n"
			usage
			exit 1
			;;
	esac

	L4T_VER_MAJOR=$(echo ${L4T_VER} | awk -F . '{print $1}')

	L4T_TOP=${HOME}/nvidia/nvidia_sdk/JetPack_${JETPACK_VER}_Linux_${JETSON_MODULE_SOC}_TARGETS/Linux_for_Tegra
	L4T_ST_FILE=${L4T_TOP}/ritrontek_status.txt

	L4T_SRC_TOP=${HOME}/nvidia/source_code/l4t_${L4T_VER}/Linux_for_Tegra/kernel_src
	L4T_SRC_OUTPUT_TOP=${HOME}/nvidia/source_code/l4t_${L4T_VER}/OUTPUT_${L4T_SRC_BRANCH_ARRAY[${BOARD_NAME}]}

	if [[ ${L4T_VER_MAJOR} -le 32 ]]; then
		if [[ ${JETSON_MODULE} == "JETSON_AGX_ORIN" ]] || \
			[[ ${JETSON_MODULE} == "JETSON_ORIN_NX" ]]; then
			echo -e "[ERROR] L4T${L4T_VER} donot support current module: ${JETSON_MODULE}\n"
			exit 1
		fi

		L4T_SRC_MAKE_TOP=${L4T_SRC_TOP}/kernel/kernel-4.9
	else
		L4T_SRC_MAKE_TOP=${L4T_SRC_TOP}/kernel/kernel-5.10
	fi

	L4T_TOP_TMP=l4t_changed_patch/JetPack${JETPACK_VER}_L4T${L4T_VER}
	L4T_CHANGED_TOP=l4t_changed_patch/JetPack${JETPACK_VER}_L4T${L4T_VER}_${BOARD_NAME}_changed
	L4T_PATCH_TOP=l4t_changed_patch/JetPack${JETPACK_VER}_L4T${L4T_VER}_${BOARD_NAME}_patch

	echo -e "[DEBUG]  JetPack : ${JETPACK_VER}"
	echo -e "[DEBUG]      L4T : ${L4T_VER}"
	echo -e "[DEBUG]    Board : ${BOARD_NAME}"
	echo -e "[DEBUG]   Module : ${JETSON_MODULE}"
	echo -e "[DEBUG]  L4T TOP : ${L4T_TOP}"
}

function sudo_cmd()
{
	if [[ $# -lt 1 ]]; then
		return
	fi

	local cmd=$@
	echo ${PASSWD} | sudo -p "-" -kS bash -c "${cmd}"
}

function sudo_cmd_check_w()
{
	if [[ $# -lt 2 ]]; then
		return
	fi

	local file=$1
	shift
	local cmd=$@

	if [[ -w ${file} ]]; then
		bash -c "${cmd}"
	else
		sudo_cmd ${cmd}
	fi
}

function show_image_dtb_status()
{
	local KERNEL=${L4T_TOP}/kernel/Image
	local DTB=${L4T_TOP}/kernel/dtb/${L4T_DTB_ARRAY[${BOARD_NAME}]}

	if [[ -L ${KERNEL} ]]; then
		local CUR_KERNEL="[Symbolic] "$(readlink ${KERNEL})
	else
		local CUR_KERNEL="[Original] "${KERNEL}
	fi
	if [[ -L ${DTB} ]]; then
		local CUR_DTB="[Symbolic] "$(readlink ${DTB})
	else
		local CUR_DTB="[Original] "${DTB}
	fi

	echo -e "[INFO] Kernel Version Magic: ${L4T_VERMAGIC}"
	echo -e "[INFO] Kernel and DTB      : $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB})"
	echo -e ">>> ${CUR_KERNEL}"
	echo -e ">>> ${CUR_DTB}\n"
}

function cmd_bsp_st()
{
	echo -e "[DEBUG] ${FUNCNAME}"
	l4t_st_op showall

	show_image_dtb_status
}

function l4t_bsp_apply_patch()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	local PATCH_DIR=$1
	if [[ -d ${PATCH_DIR} ]]; then
		traverse ${PATCH_DIR} traverse_func_bsp_install
	fi
}

function l4t_bsp_init()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	if [[ -d ${L4T_TOP} ]]; then
		if [[ ! -f ${L4T_ST_FILE} ]]; then
			echo -e "[WARN] Ritrontek status file does not exist in JetPack L4T directory:"
			echo -e ">>> ${L4T_ST_FILE}"
			read -p "Init L4T status file now? (Y/n):" key
			if [[ ${key} == "n" || ${key} == "N" ]]; then
				exit 0
			fi

			l4t_st_op init
		fi

		if [[ ${L4T_VER} != $(l4t_st_op get ${L4T_ST_KEY_VER}) ]]; then
			echo -e "[ERROR] L4T VER mismatch\n"
			l4t_st_op showall
			exit 1
		fi

		# Only when KERNEL_DTB == BSP and STATUS == NONE,
		# allow switch L4T BSP to another BOARD
		if [[ ${BOARD_NAME} != $(l4t_st_op get ${L4T_ST_KEY_BOARD}) ]]; then
			if [[ $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) != "BSP" \
				|| $(l4t_st_op get ${L4T_ST_KEY_STATUS}) != "NONE" ]]; then

				echo -e "[ERROR] Board name mismatch, and L4T current status cannot switch to another board\n"
				l4t_st_op showall
				exit 1
			fi

			echo -e "[INFO] Switch to new Board: $(l4t_st_op get ${L4T_ST_KEY_BOARD}) -> ${BOARD_NAME}"
			l4t_st_op set ${L4T_ST_KEY_BOARD} ${BOARD_NAME}
		fi
	else
		echo -e "[WARN] JetPack L4T directory does not exist: ${L4T_TOP}"
		read -p "Init JetPack L4T directory now? (Y/n):" key

		if [[ ${key} == "n" || ${key} == "N" ]]; then
			echo -e "[INFO] Exit"
			exit 0
		fi

		local L4T_PKG=${HOME}/Downloads/nvidia/sdkm_downloads/Jetson_Linux_R${L4T_VER}_aarch64.tbz2
		local ROOTFS_PKG=${HOME}/Downloads/nvidia/sdkm_downloads/Tegra_Linux_Sample-Root-Filesystem_R${L4T_VER}_aarch64.tbz2

		if [[ ! -f ${L4T_PKG} ]]; then
			echo -e "[ERROR] JetPack L4T package doesnot exist, please download first:"
			echo -e ">>> ${L4T_PKG}\n"
			exit 1
		fi
		if [[ ! -f ${ROOTFS_PKG} ]]; then
			echo -e "[ERROR] JetPack L4T ROOTFS package doesnot exist, please download first:"
			echo -e ">>> ${ROOTFS_PKG}\n"
			exit 1
		fi

		echo -e "[INFO] Decompress L4T Pakcage: $(basename ${L4T_PKG}) ..."
		mkdir -p $(dirname ${L4T_TOP})
		tar -jxf ${L4T_PKG} -C $(dirname ${L4T_TOP})

		echo -e "[INFO] Decompress ROOTFS Pakcage: $(basename ${ROOTFS_PKG}) ..."
		sudo_cmd "tar -jxf ${ROOTFS_PKG} -C ${L4T_TOP}/rootfs"

		local SECBOOT_PKG=${HOME}/Downloads/nvidia/sdkm_downloads/secureboot_R${L4T_VER}_aarch64.tbz2
		if [[ -f ${SECBOOT_PKG} ]]; then
			echo -e "[INFO] Decompress SECBOOT Pakcage: $(basename ${SECBOOT_PKG}) ..."
			tar -jxf ${SECBOOT_PKG} -C ${L4T_TOP}/..
		else
			echo -e "\n[WARN] The JetPack SECBOOT package doesnot exist."
			echo -e ">>> ${SECBOOT_PKG}"
		fi

		echo -e "\n[INFO] Apply NVIDIA binaries ..."
		sudo_cmd "${L4T_TOP}/apply_binaries.sh"

		local NVIDIA_BSP_PATCH=l4t_bsp_patch/NVIDIA_JetPack${JETPACK_VER}_L4T${L4T_VER}_patch
		if [[ -d ${NVIDIA_BSP_PATCH} ]]; then
			echo -e "\n[INFO] Apply NVIDIA BSP Patches ..."
			l4t_bsp_apply_patch ${NVIDIA_BSP_PATCH}
		fi

		local RK_BSP_PATCH=l4t_bsp_patch/RK_JetPack${JETPACK_VER}_L4T${L4T_VER}_patch
		if [[ -d ${RK_BSP_PATCH} ]]; then
			echo -e "\n[INFO] Apply Ritrontek BSP Patches ..."
			l4t_bsp_apply_patch ${RK_BSP_PATCH}
		fi

		if [[ -L ${BASH_SOURCE[0]} ]]; then
			local SCRIPT_DIR=$(cd $(dirname -- $(readlink ${BASH_SOURCE[0]})) &> /dev/null && pwd)
		else
			local SCRIPT_DIR=$(cd $(dirname -- ${BASH_SOURCE[0]}) &> /dev/null && pwd)
		fi
		ln -sf ${SCRIPT_DIR}/flash_nvme_drive.sh ${L4T_TOP}/

		l4t_st_op init
		echo -e "\n[INFO] L4T BSP directory init successfully.\n"
	fi

	L4T_VERMAGIC=$(strings ${L4T_TOP}/kernel/Image | grep -i "Linux version [0-9]" | awk '{print $3}')
}

function traverse_func_bsp_release()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	local changed_file=$1
	local release_res_dir=$2
	local release_file=${release_res_dir}/$(echo ${changed_file} | cut -d/ -f3-)

	# Due to all rootfs directory has released,
	# so skip release rootfs stuff in l4t_changed_patch directory.
	if [[ $(echo ${changed_file} | cut -d/ -f3) == "rootfs" ]]; then
		return 0
	fi

	echo -e "[INFO] >>> ${release_file}"

	if [[ ! -d $(dirname ${release_file}) ]]; then
		mkdir -p $(dirname ${release_file})
	fi

	if [[ ${changed_file} =~ \.dts$ ]]; then
		dtc -I dts -O dtb ${changed_file} -o ${release_file:0:${#release_file}-1}b &>/dev/null
	else
		cp ${changed_file} ${release_file}
	fi
}

function cmd_bsp_release()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	if [[ ${BOARD_NAME} =~ ^DEVKIT- ]]; then
		echo -e "[ERROR] This function doesnot support DEVKIT\n"
		exit 1
	fi

	if [[ $(l4t_st_op get ${L4T_ST_KEY_VER}) != ${L4T_VER} \
		|| $(l4t_st_op get ${L4T_ST_KEY_BOARD}) != ${BOARD_NAME} \
		|| $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) == "BSP" \
		|| $(l4t_st_op get ${L4T_ST_KEY_STATUS}) == "NONE" ]]; then

		echo -e "[ERROR] L4T error status to release\n"
		l4t_st_op showall
		exit 1
	fi

	local RK_VER_SUFFIX=$(echo ${L4T_VERMAGIC} | cut -d- -f3-)
	if [[ -z ${RK_VER_SUFFIX} ]]; then
		echo -e "[ERROR] Current released kernel version magic doesnot included \"RK_VER_SUFFIX\"\n"
		exit 1
	fi

	echo -e "[INFO] Choose which type rootfs released into package:"
	echo -e ">>> [1-none      ] do not release rootfs"
	echo -e ">>> [2-rootfs    ] release rootfs from the L4T BSP roofs directory"
	echo -e ">>> [3-system.img] release rootfs system.img reading from the Jetson Device"
	read -p "Please input \"1, 2, 3\" to select: " key
	while [[ ${key} != "1" ]] \
		&& [[ ${key} != "2" ]] \
		&& [[ ${key} != "3" ]];
	do
		echo -e "\n[ERROR] Input error!"
		read -p "Please input \"1, 2, 3\" to select: " key
	done

	local L4T_FLASH_OPT=
	local L4T_FLASH_TARGET=mmcblk0p1
	case ${key} in
	"1")
		echo -e "[INFO] Selected 1-none. Do not release rootfs"
		local L4T_RELEASE_ROOTFS=none
		local L4T_RELEASE_PKG=Ritrontek_NOROOTFS_JetPack${JETPACK_VER}_L4T${L4T_VER}_${BOARD_NAME}_${RK_VER_SUFFIX}.tbz2
		L4T_FLASH_TARGET=nvme0n1p1
		;;
	"2")
		echo -e "[INFO] Selected 2-rootfs"
		local L4T_RELEASE_ROOTFS=rootfs
		local L4T_RELEASE_PKG=Ritrontek_ROOTFS_JetPack${JETPACK_VER}_L4T${L4T_VER}_${BOARD_NAME}_${RK_VER_SUFFIX}.tbz2
		;;
	"3")
		echo -e "[INFO] Selected 3-system.img"
		local L4T_RELEASE_ROOTFS=system.img
		local L4T_RELEASE_PKG=Ritrontek_IMG_JetPack${JETPACK_VER}_L4T${L4T_VER}_${BOARD_NAME}_${RK_VER_SUFFIX}.tbz2
		L4T_FLASH_OPT="-r"

		local board_usb=$(lsusb -d 0x0955:)
		if [[ -z ${board_usb} ]]; then
			echo -e "[INFO] Please connect the target Jetson Device to HOST, and put it into recovery mode."
			read -p "Press any key to continue ..." key

			local board_usb=$(lsusb -d 0x0955:)
			if [[ -z ${board_usb} ]]; then
				echo -e "[ERROR] Jetson Board does not connect, cannot clone \"system.img\", exit!"
				exit 1
			fi
		fi
		;;
	*)
		echo -e "\n[ERROR] Input error!\n"
		exit 1
		;;
	esac

	local L4T_RELEASE_DIR=$(echo ${L4T_RELEASE_PKG:0:${#L4T_RELEASE_PKG}-5})

	echo -e "[INFO] Release L4T BSP into a package:"
	if [[ -f ${L4T_RELEASE_PKG} ]]; then
		rm ${L4T_RELEASE_PKG}
	fi
	if [[ -d ${L4T_RELEASE_DIR} ]]; then
		rm -rf ${L4T_RELEASE_DIR}/*
	else
		mkdir ${L4T_RELEASE_DIR}
	fi

	echo -e "[INFO] Package files ..."

	# kernel
	local release_file=${L4T_RELEASE_DIR}/resources/kernel/Image
	local bsp_file=${L4T_TOP}/$(echo ${release_file} | cut -d/ -f3-)
	echo -e "[INFO] >>> ${release_file}"
	mkdir -p $(dirname ${release_file})
	cp ${bsp_file} ${release_file}

	# dtb
	release_file=${L4T_RELEASE_DIR}/resources/kernel/dtb/${L4T_DTB_ARRAY[${BOARD_NAME}]}
	bsp_file=${L4T_TOP}/$(echo ${release_file} | cut -d/ -f3-)
	echo -e "[INFO] >>> ${release_file}"
	mkdir -p $(dirname ${release_file})
	cp ${bsp_file} ${release_file}

	# rootfs or system.img
	if [[ ${L4T_RELEASE_ROOTFS} == "rootfs" ]]; then
		release_file=${L4T_RELEASE_DIR}/resources/rootfs.tbz2
		echo -e "[INFO] >>> ${release_file}"
		sudo_cmd "tar -jcf ${release_file} -C ${L4T_TOP} rootfs"
	elif [[ ${L4T_RELEASE_ROOTFS} == "system.img" ]]; then
		release_file=${L4T_RELEASE_DIR}/resources/bootloader/system.img
		echo -e "[INFO] >>> ${release_file}"
		echo -e "[INFO] Clone \"system.img\" from the target Jetson Devcie mmcblk0p1 ..."
		cd ${L4T_TOP}
		sudo_cmd "./flash.sh -r -k APP -G released_system.img ${L4T_CONF_ARRAY[${BOARD_NAME}]} mmcblk0p1"
		ret=$?
		cd -
		if [[ ${ret} != 0 ]]; then
			echo -e "[ERROR] Clone the target Jetson Devcie APP error, exit!"
			sudo_cmd "rm -rf ${L4T_RELEASE_DIR}"
			exit 1
		fi

		mkdir -p $(dirname ${release_file})
		cp ${L4T_TOP}/released_system.img ${release_file}
	fi

	# l4t changed
	traverse ${L4T_CHANGED_TOP} traverse_func_bsp_release ${L4T_RELEASE_DIR}/resources

	local L4T_FLASH_CMD="sudo ./flash.sh ${L4T_FLASH_OPT} ${L4T_CONF_ARRAY[${BOARD_NAME}]} ${L4T_FLASH_TARGET}"

	# copy install.sh into release package
	cp l4t_rk_pkg_tools/install.sh ${L4T_RELEASE_DIR}/install.sh
	sed -i "s|<_L4T_FLASH_CMD_>|${L4T_FLASH_CMD}|g" ${L4T_RELEASE_DIR}/install.sh
	sed -i "s|<_L4T_BSP_FIX_CMD_>|${L4T_BSP_FIX_CMD}|g" ${L4T_RELEASE_DIR}/install.sh
	chmod a+x ${L4T_RELEASE_DIR}/install.sh

	# copy README.md into release package
	cp l4t_rk_pkg_tools/README.md ${L4T_RELEASE_DIR}/README.md
	sed -i "s/<_JETPACK_VER_>/${JETPACK_VER}/g" ${L4T_RELEASE_DIR}/README.md
	local _L4T_TOP_SUBSTR_="~/$(echo ${L4T_TOP} | cut -d/ -f4-)"
	sed -i "s|<_L4T_TOP_>|${_L4T_TOP_SUBSTR_}|g" ${L4T_RELEASE_DIR}/README.md
	sed -i "s/<_RK_BSP_PKG_DIR_>/${L4T_RELEASE_DIR}/g" ${L4T_RELEASE_DIR}/README.md
	sed -i "s|<_L4T_FLASH_CMD_>|${L4T_FLASH_CMD}|g" ${L4T_RELEASE_DIR}/README.md

	# copy ChangeLog.txt into release package
	local changelog=l4t_rk_pkg_tools/${BOARD_NAME}_ChangeLog.txt
	if [[ -f ${changelog} ]]; then
		cp ${changelog} ${L4T_RELEASE_DIR}/ChangeLog.txt
		sed -i "s/<_RK_BSP_VER_>/${RK_VER_SUFFIX}/g" ${L4T_RELEASE_DIR}/ChangeLog.txt
		local RK_RELEASE_DATE=$(echo ${RK_VER_SUFFIX} | cut -b1-8)
		sed -i "s/<_DATE_>/${RK_RELEASE_DATE}/g" ${L4T_RELEASE_DIR}/ChangeLog.txt
	fi

	# compress
	echo -e "[INFO] Compress into a package ..."
	tar -jcf ${L4T_RELEASE_PKG} ${L4T_RELEASE_DIR}
	#rm -rf ${L4T_RELEASE_DIR}

	echo -e "\n[INFO] Release Done:"
	echo -e "[INFO] >>> ${L4T_RELEASE_PKG}\n"
}

function traverse_func_bsp_install()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	local release_file=$1
	local release_file_name=$(basename ${release_file})

	echo -e "[INFO] >>> release_file=${release_file}"

	if [[ ${release_file_name} =~ ^rootfs.*\.tbz2$ ]]; then
		local bsp_file=${L4T_TOP}/$(echo ${release_file_name:0:${#release_file_name}-5} | sed 's/_/\//g')
		echo -e "[INFO] >>> bsp_file=${bsp_file}"

		if [[ -d ${bsp_file} || -f ${bsp_file} ]]; then
			sudo_cmd "mv ${bsp_file} ${bsp_file}.bak"
		fi
		sudo_cmd "tar -jxf ${release_file} -C ${L4T_TOP}"
	elif [[ ${release_file_name} =~ ^rootfs.*$ ]]; then
		local bsp_file=${L4T_TOP}/$(echo ${release_file_name} | sed 's/_/\//g')
		echo -e "[INFO] >>> bsp_file=${bsp_file}"

		if [[ -d ${bsp_file} || -f ${bsp_file} ]]; then
			sudo_cmd "mv ${bsp_file} ${bsp_file}.bak"
		fi
		sudo_cmd "cp -r ${release_file} ${bsp_file}"
	elif [[ ${release_file_name} =~ ^.*\.tbz2$ ]]; then
		sudo_cmd "tar -jxf ${release_file} -C ${L4T_TOP}"
	else
		if [[ $(echo ${release_file} | cut -d/ -f2) == "resources" ]] \
			|| [[ $(echo ${release_file} | cut -d/ -f2) =~ ^NVIDIA_ ]] \
			|| [[ $(echo ${release_file} | cut -d/ -f2) =~ ^RK_ ]]; then
			local bsp_file=${L4T_TOP}/$(echo ${release_file} | cut -d/ -f3-)
		else
			local bsp_file=${L4T_TOP}/$(echo ${release_file} | cut -d/ -f2-)
		fi

		echo -e "[INFO] >>> bsp_file=${bsp_file}"

		# skip files
		if [[ ${release_file_name} == "install.sh" ]] \
			|| [[ ${release_file_name} == "README.md" ]]; then
			echo -e "[INFO] skipped to install: ${release_file}"
			return 0
		fi

		if [[ -f ${bsp_file} ]]; then
			sudo_cmd_check_w $(dirname ${bsp_file}) "mv ${bsp_file} ${bsp_file}.bak"
		fi
		sudo_cmd_check_w $(dirname ${bsp_file}) "cp ${release_file} ${bsp_file}"
	fi
}

function cmd_bsp_install()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	if [[ $# -le 0 ]]; then
		echo -e "\n[ERROR] Must specify a release package: Ritrontek_xxx.tbz2\n"
		exit 1
	fi

	if [[ ${BOARD_NAME} =~ ^DEVKIT- ]]; then
		echo -e "\n[ERROR] This function doesnot support DEVKIT-xxx."
		exit 1
	fi

	if [[ $(l4t_st_op get ${L4T_ST_KEY_VER}) != ${L4T_VER} \
		|| $(l4t_st_op get ${L4T_ST_KEY_BOARD}) != ${BOARD_NAME} \
		|| $(l4t_st_op get ${L4T_ST_KEY_STATUS}) == "INSTALLED" ]]; then

		echo -e "[ERROR] L4T error status to install, please confirm:"
		l4t_st_op showall
		exit 1
	fi

	if [[ $(dirname $1) != "." ]]; then
		echo -e "[ERROR] L4T_RELEASE_PKG/L4T_RELEASE_DIR must stay in current work directory.\n"
		exit 1
	fi

	if [[ $(basename $1) =~ ^.*\.tbz2$ ]]; then
		local L4T_RELEASE_PKG=$(basename $1)
		local L4T_RELEASE_DIR=$(echo ${L4T_RELEASE_PKG:0:${#L4T_RELEASE_PKG}-5})
	else
		local L4T_RELEASE_DIR=$(basename $1)
		local L4T_RELEASE_PKG=$(basename $1).tbz2
	fi

	if [[ ! ${L4T_RELEASE_PKG} =~ \
		^Ritrontek_JetPack${JETPACK_VER}_L4T${L4T_VER}_${BOARD_NAME}_[0-9]{14}\.tbz2$ ]] \
		&& [[ ! ${L4T_RELEASE_PKG} =~ \
		^Ritrontek_IMG_JetPack${JETPACK_VER}_L4T${L4T_VER}_${BOARD_NAME}_[0-9]{14}\.tbz2$ ]]; then

		echo -e "[ERROR] Installed package donot match current L4T BSP, please confirm:"
		echo -e ">>> ${L4T_RELEASE_PKG}\n"
		exit 1
	fi

	echo -e "\n[INFO] Installed package into L4T BSP"
	if [[ ! -d ${L4T_RELEASE_DIR} ]]; then
		if [[ ! -f ${L4T_RELEASE_PKG} ]]; then
			echo -e "[ERROR] L4T release package doesnot exist, please confirm:"
			echo -e ">>> ${L4T_RELEASE_PKG}\n"
			exit 1
		else
			echo -e "[INFO] Decompress package ..."
			if [[ $(tar -tf ${L4T_RELEASE_PKG} | head -n 1) == "./" ]]; then
				mkdir ${L4T_RELEASE_DIR}
				tar -jxf ${L4T_RELEASE_PKG} -C ${L4T_RELEASE_DIR}
			else
				tar -jxf ${L4T_RELEASE_PKG}
			fi
		fi
	fi

	if [[ -d ${L4T_RELEASE_DIR}/resources ]]; then
		traverse ${L4T_RELEASE_DIR}/resources traverse_func_bsp_install
	else
		traverse ${L4T_RELEASE_DIR} traverse_func_bsp_install
	fi

	local PREV_VALUE="$(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB})"
	PREV_VALUE+=":$(l4t_st_op get ${L4T_ST_KEY_STATUS})"
	PREV_VALUE+=":$(l4t_st_op get ${L4T_ST_KEY_OPT})"

	l4t_st_op set ${L4T_ST_KEY_KERNEL_DTB} "PKG"
	l4t_st_op set ${L4T_ST_KEY_STATUS} "INSTALLED"
	l4t_st_op set ${L4T_ST_KEY_OPT} ${L4T_RELEASE_PKG}
	l4t_st_op set ${L4T_ST_KEY_PREV} ${PREV_VALUE}
	echo -e "\n[INFO] Package intalled successfully.\n"
}

function traverse_func_bsp_uninstall()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	local release_file=$1
	local release_file_name=$(basename ${release_file})

	echo -e "[INFO] >>> release_file=${release_file}"

	if [[ ${release_file_name} =~ ^rootfs.*\.tbz2$ ]]; then
		local bsp_file=${L4T_TOP}/$(echo ${release_file_name:0:${#release_file_name}-5} | sed 's/_/\//g')
	elif [[ ${release_file_name} =~ ^rootfs.*$ ]]; then
		local bsp_file=${L4T_TOP}/$(echo ${release_file_name} | sed 's/_/\//g')
	else
		if [[ $(echo ${release_file} | cut -d/ -f2) == "resources" ]]; then
			local bsp_file=${L4T_TOP}/$(echo ${release_file} | cut -d/ -f3-)
		else
			local bsp_file=${L4T_TOP}/$(echo ${release_file} | cut -d/ -f2-)
		fi
	fi

	echo -e "[INFO] >>> bsp_file=${bsp_file}"

	sudo_cmd "rm -rf ${bsp_file}"
	if [[ -d ${bsp_file}.bak || -f ${bsp_file}.bak ]]; then
		sudo_cmd_check_w $(dirname ${bsp_file}) "mv ${bsp_file}.bak ${bsp_file}"
	fi
}

function cmd_bsp_uninstall()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	if [[ ${BOARD_NAME} =~ ^DEVKIT- ]]; then
		echo -e "[ERROR] This function doesnot support DEVKIT."
		exit 1
	fi

	if [[ $(l4t_st_op get ${L4T_ST_KEY_VER}) != ${L4T_VER} \
		|| $(l4t_st_op get ${L4T_ST_KEY_BOARD}) != ${BOARD_NAME} \
		|| $(l4t_st_op get ${L4T_ST_KEY_STATUS}) != "INSTALLED" ]]; then

		echo -e "[ERROR] L4T error status to uninstall, please confirm:"
		l4t_st_op showall
		exit 1
	fi

	echo -e "\n[INFO] Uninstalled package from L4T BSP"

	local L4T_RELEASE_PKG=$(l4t_st_op get ${L4T_ST_KEY_OPT})
	local L4T_RELEASE_DIR=$(echo ${L4T_RELEASE_PKG:0:${#L4T_RELEASE_PKG}-5})
	if [[ ! -d ${L4T_RELEASE_DIR} ]]; then
		if [[ ! -f ${L4T_RELEASE_PKG} ]]; then
			echo -e "[ERROR] L4T release package doesnot exist, please confirm:"
			echo -e ">>> ${L4T_RELEASE_PKG}\n"
			exit 1
		else
			echo -e "[INFO] Decompress package ..."
			if [[ $(tar -tf ${L4T_RELEASE_PKG} | head -n 1) == "./" ]]; then
				mkdir ${L4T_RELEASE_DIR}
				tar -jxf ${L4T_RELEASE_PKG} -C ${L4T_RELEASE_DIR}
			else
				tar -jxf ${L4T_RELEASE_PKG}
			fi
		fi
	fi

	if [[ -d ${L4T_RELEASE_DIR}/resources ]]; then
		traverse ${L4T_RELEASE_DIR}/resources traverse_func_bsp_uninstall
	else
		traverse ${L4T_RELEASE_DIR} traverse_func_bsp_uninstall
	fi

	local PREV_VALUE=$(l4t_st_op get ${L4T_ST_KEY_PREV})

	l4t_st_op set ${L4T_ST_KEY_KERNEL_DTB} $(echo ${PREV_VALUE} | awk -F ':' '{print $1}')
	l4t_st_op set ${L4T_ST_KEY_STATUS} $(echo ${PREV_VALUE} | awk -F ':' '{print $2}')
	l4t_st_op set ${L4T_ST_KEY_OPT} $(echo ${PREV_VALUE} | awk -F ':' '{print $3}')
	l4t_st_op set ${L4T_ST_KEY_PREV} "NONE"

	echo -e "[INFO] Package uninstalled successfully\n"
}

function cmd_bsp_backup()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	local RK_VER_SUFFIX=$(echo ${L4T_VERMAGIC} | cut -d- -f3-)
	if [[ -z ${RK_VER_SUFFIX} ]]; then
		echo -e "[WARN] Current kernel version magic doesnot include \"RK_VER_SUFFIX\"\n"
		RK_VER_SUFFIX=NONE
	fi

	local L4T_BACKUP_PKG=Ritrontek_BACKUP_JetPack${JETPACK_VER}_L4T${L4T_VER}_${BOARD_NAME}_${RK_VER_SUFFIX}.tbz2
	local L4T_BACKUP_DIR=$(echo ${L4T_BACKUP_PKG:0:${#L4T_BACKUP_PKG}-5})
	local L4T_BACKUP_INSTALL_DIR=${L4T_TOP}/tools/backup_restore
	local L4T_BACKUP_IMAGES=${L4T_BACKUP_INSTALL_DIR}/images

	if [[ -f ${L4T_BACKUP_PKG} ]]; then
		sudo_cmd "rm -f ${L4T_BACKUP_PKG}"
	fi
	if [[ -d ${L4T_BACKUP_DIR} ]]; then
		sudo_cmd "rm -rf ${L4T_BACKUP_DIR}"
	fi

	local perform_backup="y";
	if [[ -d ${L4T_BACKUP_IMAGES} ]]; then
		echo "[INFO] Backup images directory already exist:"
		echo "[INFO] >>> ${L4T_BACKUP_IMAGES}"
		read -t 10 -p "Using the exist images? (y/N) 10sec:" key

		if [[ ${key} == "Y" || ${key} == "y" ]]; then
			perform_backup="n"
		else
			perform_backup="y"
			echo -e "[INFO] Remove the exist images"
			sudo_cmd "rm -rf ${L4T_BACKUP_IMAGES}"
		fi
	fi

	if [[ ${perform_backup} == "y" ]]; then
		local board_usb=$(lsusb -d 0x0955:)
		if [[ -z ${board_usb} ]]; then
			echo -e "[INFO] Please connect the Jetson Device to HOST, and put it into recovery mode."
			read -p "Press any key to continue ..." key

			local board_usb=$(lsusb -d 0x0955:)
			if [[ -z ${board_usb} ]]; then
				echo -e "[ERROR] Jetson Board does not connect, cannot flash, exit!"
				exit 1
			fi
		fi

		if [[ ${JETSON_MODULE} =~ ^JETSON_XAVIER_NX ]] \
			|| [[ ${JETSON_MODULE} =~ ^JETSON_ORIN_NX ]]; then
			sudo_cmd "sed -i \"s/mmcblk0/nvme0n1/g\" ${L4T_BACKUP_INSTALL_DIR}/nvbackup_partitions.sh"
			sudo_cmd "sed -i \"s/mmcblk0/nvme0n1/g\" ${L4T_BACKUP_INSTALL_DIR}/nvrestore_partitions.sh"
		fi

		local BACKUP_CMD="cd ${L4T_TOP} && ./tools/backup_restore/l4t_backup_restore.sh -b ${L4T_CONF_ARRAY[${BOARD_NAME}]}"
		echo -e "${BACKUP_CMD}"
		sudo_cmd "${BACKUP_CMD}"
		local ret=$?
		if [[ ${ret} != 0 ]]; then
			echo -e "[ERROR] Backup Device error:"
			echo -e ">>> ${BACKUP_CMD}\n"
			exit 1
		fi
	fi

	if [[ -d ${L4T_BACKUP_DIR} ]]; then
		rm -rf ${L4T_BACKUP_DIR}/*
	else
		mkdir ${L4T_BACKUP_DIR}
	fi

	echo -e "[INFO] Copy backup files ..."

	# conf
	local backup_file=${L4T_BACKUP_DIR}/resources/${L4T_CONF_ARRAY[${BOARD_NAME}]}.conf
	local bsp_file=${L4T_TOP}/$(echo ${backup_file} | cut -d/ -f3-)
	echo -e "[INFO] >>> ${backup_file}"
	mkdir -p $(dirname ${backup_file})
	cp ${bsp_file} ${backup_file}

	# dtb
	backup_file=${L4T_BACKUP_DIR}/resources/kernel/dtb/${L4T_DTB_ARRAY[${BOARD_NAME}]}
	bsp_file=${L4T_TOP}/$(echo ${backup_file} | cut -d/ -f3-)
	echo -e "[INFO] >>> ${backup_file}"
	mkdir -p $(dirname ${backup_file})
	cp ${bsp_file} ${backup_file}

	# backup images
	backup_file=${L4T_BACKUP_DIR}/resources/images.tbz2
	bsp_file=${L4T_TOP}/$(echo ${backup_file} | cut -d/ -f3-)
	echo -e "[INFO] >>> ${backup_file}"
	mkdir -p $(dirname ${backup_file})
	tar -jcf ${backup_file} -C ${L4T_TOP} tools/backup_restore/images

	local L4T_FLASH_CMD="sudo ./tools/backup_restore/l4t_backup_restore.sh -r ${L4T_CONF_ARRAY[${BOARD_NAME}]}"

	# copy install.sh into backup package
	cp l4t_rk_pkg_tools/install.sh ${L4T_BACKUP_DIR}/install.sh
	sed -i "s|<_L4T_FLASH_CMD_>|${L4T_FLASH_CMD}|g" ${L4T_BACKUP_DIR}/install.sh
	if [[ ${JETSON_MODULE} =~ ^JETSON_XAVIER_NX ]] \
		|| [[ ${JETSON_MODULE} =~ ^JETSON_ORIN_NX ]]; then
		L4T_BSP_FIX_CMD="sed -i \"s/mmcblk0/nvme0n1/g\" ../tools/backup_restore/nvbackup_partitions.sh"
		L4T_BSP_FIX_CMD+="\n\tsed -i \"s/mmcblk0/nvme0n1/g\" ../tools/backup_restore/nvrestore_partitions.sh"
	fi
	sed -i "s|<_L4T_BSP_FIX_CMD_>|${L4T_BSP_FIX_CMD}|g" ${L4T_BACKUP_DIR}/install.sh
	chmod a+x ${L4T_BACKUP_DIR}/install.sh

	# copy README.md into backup package
	cp l4t_rk_pkg_tools/README.md ${L4T_BACKUP_DIR}/README.md
	sed -i "s/<_JETPACK_VER_>/${JETPACK_VER}/g" ${L4T_BACKUP_DIR}/README.md
	local _L4T_TOP_SUBSTR_="~/$(echo ${L4T_TOP} | cut -d/ -f4-)"
	sed -i "s|<_L4T_TOP_>|${_L4T_TOP_SUBSTR_}|g" ${L4T_BACKUP_DIR}/README.md
	sed -i "s/<_RK_BSP_PKG_DIR_>/${L4T_BACKUP_DIR}/g" ${L4T_BACKUP_DIR}/README.md
	sed -i "s|<_L4T_FLASH_CMD_>|${L4T_FLASH_CMD}|g" ${L4T_BACKUP_DIR}/README.md

	# copy ChangeLog.txt into backup package
	local changelog=l4t_rk_pkg_tools/${BOARD_NAME}_ChangeLog.txt
	if [[ -f ${changelog} ]]; then
		cp ${changelog} ${L4T_BACKUP_DIR}/ChangeLog.txt
		sed -i "s/<_RK_BSP_VER_>/${RK_VER_SUFFIX}/g" ${L4T_BACKUP_DIR}/ChangeLog.txt
		local RK_BACKUP_DATE=$(echo ${RK_VER_SUFFIX} | cut -b1-8)
		sed -i "s/<_DATE_>/${RK_BACKUP_DATE}/g" ${L4T_BACKUP_DIR}/ChangeLog.txt
	fi

	# compress
	echo -e "\n[INFO] Compress into a package ..."
	echo -e "[INFO] >>> ${L4T_BACKUP_PKG}"
	tar -jcf ${L4T_BACKUP_PKG} ${L4T_BACKUP_DIR}
	#rm -rf ${L4T_BACKUP_DIR}

	echo -e "\n[INFO] Backup Device Done"
}

function cmd_bsp_restore()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	if [[ $(dirname $1) != "." ]]; then
		echo -e "[ERROR] L4T_BACKUP_PKG/L4T_BACKUP_DIR must stay in current work directory.\n"
		exit 1
	fi

	if [[ $(basename $1) =~ ^.*\.tbz2$ ]]; then
		local L4T_BACKUP_PKG=$(basename $1)
		local L4T_BACKUP_DIR=$(echo ${L4T_BACKUP_PKG:0:${#L4T_BACKUP_PKG}-5})
	else
		local L4T_BACKUP_DIR=$(basename $1)
		local L4T_BACKUP_PKG=$(basename $1).tbz2
	fi

	if [[ ! ${L4T_BACKUP_PKG} =~ \
		^Ritrontek_BACKUP_JetPack${JETPACK_VER}_L4T${L4T_VER}_${BOARD_NAME}_[0-9]{14}\.tbz2$ ]]; then

		echo -e "[ERROR] BACKUP package donot match current L4T BSP, please confirm:"
		echo -e ">>> ${L4T_BACKUP_PKG}\n"
		exit 1
	fi

	if [[ ! -d ${L4T_BACKUP_DIR} ]]; then
		tar -jxf ${L4T_BACKUP_PKG}
	fi

	local L4T_BACKUP_INSTALL_DIR=${L4T_TOP}/tools/backup_restore
	local L4T_BACKUP_IMAGES=${L4T_BACKUP_INSTALL_DIR}/images
	if [[ -d ${L4T_BACKUP_IMAGES} ]]; then
		echo "[INFO] Remove the exist images directory:"
		echo ">>> ${L4T_BACKUP_IMAGES}"
		sudo_cmd "rm -rf ${L4T_BACKUP_IMAGES}"
	fi

	if [[ -d ${L4T_BACKUP_DIR}/resources ]]; then
		echo "[INFO] Install backup package ..."
		traverse ${L4T_BACKUP_DIR}/resources traverse_func_bsp_install
		echo "[INFO] Install Done."
	fi

	read -t 10 -p "Perform restore to device now? (Y/n) 10sec:" key
	if [[ ${key} == "N" || ${key} == "n" ]]; then
		echo -e "[INFO] Exit"
		exit 0
	fi

	local board_usb=$(lsusb -d 0x0955:)
	if [[ -z ${board_usb} ]]; then
		echo -e "[INFO] Please connect the Jetson Device to HOST, and put it into recovery mode."
		read -p "Press any key to continue ..." key

		local board_usb=$(lsusb -d 0x0955:)
		if [[ -z ${board_usb} ]]; then
			echo -e "[ERROR] Jetson Board does not connect, cannot flash, exit!"
			exit 1
		fi
	fi

	if [[ ${JETSON_MODULE} =~ ^JETSON_XAVIER_NX ]] \
		|| [[ ${JETSON_MODULE} =~ ^JETSON_ORIN_NX ]]; then
		sudo_cmd "sed -i \"s/mmcblk0/nvme0n1/g\" ${L4T_BACKUP_INSTALL_DIR}/nvbackup_partitions.sh"
		sudo_cmd "sed -i \"s/mmcblk0/nvme0n1/g\" ${L4T_BACKUP_INSTALL_DIR}/nvrestore_partitions.sh"
	fi

	local RESTORE_CMD="cd ${L4T_TOP} && ./tools/backup_restore/l4t_backup_restore.sh -r ${L4T_CONF_ARRAY[${BOARD_NAME}]}"
	echo -e "${RESTORE_CMD}"
	sudo_cmd "${RESTORE_CMD}"
	local ret=$?
	if [[ ${ret} != 0 ]]; then
		echo -e "[ERROR] Restore Device error:"
		echo -e ">>> ${RESTORE_CMD}\n"
		exit 1
	fi

	echo -e "\n[INFO] Restore Device Done"
}

function l4t_src_init()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	if [[ -d ${L4T_SRC_TOP} ]]; then
		echo -e "[INFO] Kernel SRC directory already exist: ${L4T_SRC_TOP}"
		echo -e "[INFO] Switch Kernel SRC branch to: ${L4T_SRC_BRANCH_ARRAY[${BOARD_NAME}]}\n"
		cd ${L4T_SRC_TOP}
		git checkout ${L4T_SRC_BRANCH_ARRAY[${BOARD_NAME}]}
		local ret=$?
		cd - >/dev/null
		if [[ ${ret} != 0 ]]; then
			echo -e "[ERROR] command error: git checkout ${L4T_SRC_BRANCH_ARRAY[${BOARD_NAME}]}\n"
			exit 1
		fi
	else
		local L4T_SRC_GIT_URL="gitea@192.168.1.55:nvidia_jetson/l4t_${L4T_VER}_kernel_src.git"

		echo -e "[INFO] Kernel SRC directory doesnot exist: ${L4T_SRC_TOP}"
		echo -e "[INFO] Clone from Gitea: ${L4T_SRC_GIT_URL}\n"
		git clone -b ${L4T_SRC_BRANCH_ARRAY[${BOARD_NAME}]} ${L4T_SRC_GIT_URL} ${L4T_SRC_TOP}
	fi
}

function cmd_src_build_dtbs()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	echo -e "[INFO] Build dtbs:"
	make -C ${L4T_SRC_MAKE_TOP} \
		dtbs -j$(nproc) \
		O=${L4T_SRC_OUTPUT_TOP} \
		ARCH=arm64 \
		CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}

	local BUILD_RET=$?
	if [[ ${BUILD_RET} -ne 0 ]]; then
		echo -e "[ERROR] L4T DTB build error!\n"
		exit 1
	fi
}

function cmd_src_build_kernel()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	echo -e "[INFO] Build Kernel Image:"
	make -C ${L4T_SRC_MAKE_TOP} \
		Image -j$(nproc) \
		O=${L4T_SRC_OUTPUT_TOP} \
		ARCH=arm64 \
		CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}

	local BUILD_RET=$?
	if [[ ${BUILD_RET} -ne 0 ]]; then
		echo -e "[ERROR] L4T Kernel build error!\n"
		exit 1
	fi
}

function cmd_src_build_modules()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	echo -e "[INFO] Build Kernel Modules:"
	make -C ${L4T_SRC_MAKE_TOP} \
		modules -j$(nproc) \
		O=${L4T_SRC_OUTPUT_TOP} \
		ARCH=arm64 \
		CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}

	local BUILD_RET=$?
	if [[ ${BUILD_RET} -ne 0 ]]; then
		echo -e "[ERROR] L4T Modules build error!\n"
		exit 1
	fi

	if [[ ${L4T_VER_MAJOR} -ge 35 ]]; then
		l4t_src_module_display build
	fi
	if [[ ${BOARD_NAME} == "RKPE32AN0" ]]; then
		l4t_src_module_ngbe build
	fi
}

function l4t_src_modules_install()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	if [[ $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) != "SRC" ]]; then
		echo -e "[ERROR] Current Kernel is not SRC, cannot operate src modules\n"
		exit 1
	fi

	if [[ -d ${L4T_TOP}/rootfs/lib/modules/${L4T_VERMAGIC} ]]; then
		echo -e "[INFO] Kernel Modules already installed: ${L4T_VERMAGIC}"
	else
		echo -e "[INFO] Install Kernel Modules ..."
		sudo_cmd "make -C ${L4T_SRC_MAKE_TOP} \
			modules_install \
			O=${L4T_SRC_OUTPUT_TOP} \
			ARCH=arm64 \
			CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]} \
			INSTALL_MOD_PATH=${L4T_TOP}/rootfs"

		if [[ ${L4T_VER_MAJOR} -ge 35 ]]; then
			l4t_src_module_display install
		fi
		if [[ ${BOARD_NAME} == "RKPE32AN0" ]]; then
			l4t_src_module_ngbe install
		fi

		sudo_cmd "depmod \
				-b ${L4T_TOP}/rootfs \
				-F ${L4T_SRC_OUTPUT_TOP}/System.map \
				${L4T_VERMAGIC}"

		echo -e "[INFO] Install Done."
	fi
}

function l4t_src_modules_uninstall()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	if [[ $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) != "SRC" ]]; then
		echo -e "[ERROR] Current Kernel is not SRC, cannot operate src modules\n"
		exit 1
	fi

	if [[ -n ${L4T_VERMAGIC} ]] \
		&& [[ $(echo ${L4T_VERMAGIC} | awk -F "-" '{print $NF}') != "tegra" ]] \
		&& [[ -d ${L4T_TOP}/rootfs/lib/modules/${L4T_VERMAGIC} ]]; then
		sudo_cmd "rm -rf ${L4T_TOP}/rootfs/lib/modules/${L4T_VERMAGIC}"
	fi
}

function l4t_src_module_display()
{
	echo -e "\n[DEBUG] ${FUNCNAME} $@"

	local L4T_SRC_OUTPUT_VERMAGIC=$(cat ${L4T_SRC_OUTPUT_TOP}/include/config/kernel.release)
	local L4T_MODULE_DISPLAY_SRC="${HOME}/nvidia/source_code/l4t_${L4T_VER}_nvidia_display_driver"
	local L4T_MODULE_DISPLAY="${HOME}/nvidia/source_code/l4t_${L4T_VER}_nvidia_display_driver/kernel-open/nvidia.ko"

	case $1 in
		"build")
			if [[ ! -d ${L4T_MODULE_DISPLAY_SRC} ]]; then
				git clone gitea@192.168.1.55:nvidia_jetson/l4t_${L4T_VER}_nvidia_display_driver.git ${L4T_MODULE_DISPLAY_SRC}
			fi

			local L4T_MODULE_DISPLAY_VERMAGIC=$(modinfo -F vermagic ${L4T_MODULE_DISPLAY} 2>/dev/null | awk '{print $1}')
			if [[ ! -f ${L4T_MODULE_DISPLAY} ]] || \
				[[ -f ${L4T_MODULE_DISPLAY} && ${L4T_MODULE_DISPLAY_VERMAGIC} != ${L4T_SRC_OUTPUT_VERMAGIC} ]];
			then
				make clean \
					-C ${L4T_MODULE_DISPLAY_SRC} \
					-j$(nproc) \
					SYSSRC=${L4T_SRC_MAKE_TOP} \
					SYSOUT=${L4T_SRC_OUTPUT_TOP} \
					ARCH=arm64 \
					CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]} \
					TARGET_ARCH=aarch64 \
					CC=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}gcc \
					LD=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}ld.bfd \
					AR=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}ar \
					CXX=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}g++ \
					OBJCOPY=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}objcopy

				make modules \
					-C ${L4T_MODULE_DISPLAY_SRC} \
					-j$(nproc) \
					SYSSRC=${L4T_SRC_MAKE_TOP} \
					SYSOUT=${L4T_SRC_OUTPUT_TOP} \
					ARCH=arm64 \
					CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]} \
					TARGET_ARCH=aarch64 \
					CC=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}gcc \
					LD=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}ld.bfd \
					AR=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}ar \
					CXX=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}g++ \
					OBJCOPY=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}objcopy

				local BUILD_RET=$?
				if [[ ${BUILD_RET} -ne 0 ]]; then
					echo -e "[ERROR] L4T Module Display build error!\n"
					exit 1
				fi
			fi
			;;
		"install")
			if [[ $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) != "SRC" ]]; then
				echo -e "[ERROR] Current Kernel is not SRC, cannot install src modules\n"
				exit 1
			fi

			if [[ ! -f ${L4T_MODULE_DISPLAY} ]]; then
				${FUNCNAME} build

				if [[ ! -f ${L4T_MODULE_DISPLAY} ]]; then
					echo -e "[ERROR] L4T Module Display install error!\n"
					echo -e "NVIDIA Display Module file does not exist: ${L4T_MODULE_DISPLAY}.\n"
					exit 1
				fi
			fi

			sudo_cmd "make modules_install \
				-C ${L4T_MODULE_DISPLAY_SRC} \
				-j$(nproc) \
				SYSSRC=${L4T_SRC_MAKE_TOP} \
				SYSOUT=${L4T_SRC_OUTPUT_TOP} \
				INSTALL_MOD_PATH=${L4T_TOP}/rootfs \
				ARCH=arm64 \
				CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]} \
				TARGET_ARCH=aarch64 \
				CC=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}gcc \
				LD=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}ld.bfd \
				AR=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}ar \
				CXX=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}g++ \
				OBJCOPY=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}objcopy"
			;;
		*)
			echo -e "[ERROR] ${FUNCNAME} argument error!\n"
			exit 1
			;;
	esac
}

function l4t_src_module_ngbe()
{
	echo -e "\n[DEBUG] ${FUNCNAME} $@"

	local L4T_SRC_OUTPUT_VERMAGIC=$(cat ${L4T_SRC_OUTPUT_TOP}/include/config/kernel.release)
	local NGBE_VER=1.2.3
	local L4T_MODULE_NGBE_TOP="${HOME}/nvidia/source_code/ngbe-${NGBE_VER}"
	local L4T_MODULE_NGBE="${L4T_MODULE_NGBE_TOP}/src/ngbe.ko"
	local L4T_MODULE_NGBE_ROOTFS_DIR="${L4T_TOP}/rootfs/lib/modules/${L4T_VERMAGIC}/kernel/drivers/net/ethernet/ngbe"

	case $1 in
		"build")
			if [[ ! -d ${L4T_MODULE_NGBE_TOP} ]]; then
				git clone gitea@192.168.1.55:pan.kang/ngbe-${NGBE_VER}.git ${L4T_MODULE_NGBE_TOP}
			fi

			local L4T_MODULE_NGBE_VERMAGIC=$(modinfo -F vermagic ${L4T_MODULE_NGBE} 2>/dev/null | awk '{print $1}')
			if [[ ! -f ${L4T_MODULE_NGBE} ]] || \
				[[ -f ${L4T_MODULE_NGBE} && ${L4T_MODULE_NGBE_VERMAGIC} != ${L4T_SRC_OUTPUT_VERMAGIC} ]];
			then
				make clean \
					-C ${L4T_MODULE_NGBE_TOP}/src \
					-j$(nproc) \
					KERNEL_SRC=${L4T_SRC_MAKE_TOP} \
					KERNEL_OUT=${L4T_SRC_OUTPUT_TOP} \
					ARCH=arm64 \
					CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}

				make \
					-C ${L4T_MODULE_NGBE_TOP}/src \
					-j$(nproc) \
					KERNEL_SRC=${L4T_SRC_MAKE_TOP} \
					KERNEL_OUT=${L4T_SRC_OUTPUT_TOP} \
					ARCH=arm64 \
					CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}

				local BUILD_RET=$?
				if [[ ${BUILD_RET} -ne 0 ]]; then
					echo -e "[ERROR] L4T Module NGBE build error!\n"
					exit 1
				fi
			fi
			;;
		"install")
			if [[ $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) != "SRC" ]]; then
				echo -e "[ERROR] Current Kernel is not SRC, cannot install src modules\n"
				exit 1
			fi

			if [[ ! -f ${L4T_MODULE_NGBE} ]]; then
				${FUNCNAME} build

				if [[ ! -f ${L4T_MODULE_NGBE} ]]; then
					echo -e "[ERROR] L4T Module NGBE install error!\n"
					echo -e "NGBE Module file does not exist: ${L4T_MODULE_NGBE}.\n"
					exit 1
				fi
			fi

			echo -e "> ${L4T_MODULE_NGBE_ROOTFS_DIR}/$(basename ${L4T_MODULE_NGBE})"
			sudo_cmd "mkdir -p ${L4T_MODULE_NGBE_ROOTFS_DIR} \
				&& cp ${L4T_MODULE_NGBE} ${L4T_MODULE_NGBE_ROOTFS_DIR}"
			;;
		*)
			echo -e "[ERROR] ${FUNCNAME} argument error!\n"
			exit 1
			;;
	esac
}

function l4t_src_apt_source_install()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	# Comment NVIDIA APT Source to disable it
	local NVIDIA_APT_SOURCE_FILE=${L4T_TOP}/rootfs/etc/apt/sources.list.d/nvidia-l4t-apt-source.list
	if [[ -f ${NVIDIA_APT_SOURCE_FILE} ]]; then
		sudo_cmd "sed -i \"s/^deb/#deb/g\" ${NVIDIA_APT_SOURCE_FILE}"
	fi

	# Add OEM APT Source: TODO
}

function l4t_src_apt_source_uninstall()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	# Uncomment NVIDIA APT Source to enable it
	local NVIDIA_APT_SOURCE_FILE=${L4T_TOP}/rootfs/etc/apt/sources.list.d/nvidia-l4t-apt-source.list
	if [[ -f ${NVIDIA_APT_SOURCE_FILE} ]]; then
		sudo_cmd "sed -i \"s/^#deb/deb/g\" ${NVIDIA_APT_SOURCE_FILE}"
	fi

	# Remove OEM APT Source: TODO
}

function cmd_src_build()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	local L4T_SRC_OUTPUT_VERMAGIC_FILE="${L4T_SRC_OUTPUT_TOP}/include/config/kernel.release"
	if [[ -f ${L4T_SRC_OUTPUT_VERMAGIC_FILE} ]]; then
		export LOCALVERSION="-$(cat ${L4T_SRC_OUTPUT_VERMAGIC_FILE} | cut -d - -f 2-)"
	else
		local RK_VER_SUFFIX=$(date +"%Y%m%d%H%M%S")
		export LOCALVERSION="-tegra-${RK_VER_SUFFIX}"
	fi

	echo -e "[DEBUG] LOCALVERSION=${LOCALVERSION}"

	if [[ ${BOARD_NAME} =~ ^DEVKIT- ]]; then
		local MAKE_CONFIG=tegra_defconfig
	else
		local MAKE_CONFIG=tegra_ritrontek_defconfig
	fi
	echo -e "[DEBUG] MAKE_CONFIG=${MAKE_CONFIG}"

	if [[ ! -f "${L4T_SRC_OUTPUT_TOP}/.config" ]]; then
		make -C ${L4T_SRC_MAKE_TOP} \
			${MAKE_CONFIG} \
			O=${L4T_SRC_OUTPUT_TOP} \
			ARCH=arm64 \
			CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}
	fi

	if [[ $# -le 0 ]]; then
		cmd_src_build_dtbs
		cmd_src_build_kernel
		cmd_src_build_modules
	else
		local BUILD_CMD=$1
		shift 1

		case ${BUILD_CMD} in
			"${CMD_SRC_BUILD_DTBS}")
				cmd_src_build_dtbs
				;;
			"${CMD_SRC_BUILD_KERNEL}")
				cmd_src_build_kernel
				;;
			"${CMD_SRC_BUILD_MODULES}")
				cmd_src_build_modules
				;;
			*)
				echo -e "[ERROR] Unsupport SRC BUILD CMD: ${SUB_CMD}\n"
				usage
				exit 1
				;;
		esac
	fi
}

function cmd_src_install()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	if [[ $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) == "PKG" \
		|| $(l4t_st_op get ${L4T_ST_KEY_STATUS}) == "INSTALLED" ]]; then

		echo -e "[ERROR] Error status, release package installed, cannot perform cmd_src_install."
		l4t_st_op showall
		exit 1
	fi

	local KERNEL_BSP=${L4T_TOP}/kernel/Image
	local KERNEL_SRC=${L4T_SRC_OUTPUT_TOP}/arch/arm64/boot/Image

	local cur=$(readlink ${KERNEL_BSP})
	if [[ ${cur} == ${KERNEL_SRC} ]]; then
		l4t_src_modules_install
		l4t_src_apt_source_install
	else
		cmd_switch_src
	fi
}

function l4t_st_op()
{
	##### L4T_ST_FILE format ##############################################
	# VER=xxx
	# BOARD=xxx
	# KERNEL_DTB=BSP/SRC/PKG/OEM
	# STATUS=NONE/PATCHED/INSTALLED
	# OPTION=NONE/<PATCHED_PACKAGE_NAME>/<INSTALLED_PACKAGE_NAME>
	# PREV=<KERNEL_DTB_VALUE>:<STATUS_VALUE>:<OPT_VALUE>
	#######################################################################

	if [[ $# -lt 1 ]]; then
		echo -e "[ERROR] Function parameter error: ${FUNCNAME} $@\n"
		exit 1
	fi

	if [[ ! -d ${L4T_TOP} ]]; then
		echo -e "[ERROR] L4T directory does not exist: ${L4T_TOP}"
		exit 1
	fi

	local SUB_CMD=$1

	local READ_LINE=
	local READ_KEY=
	local READ_VALUE=
	local FLAG=

	if [[ ${SUB_CMD} == "init" ]]; then
		rm -f ${L4T_ST_FILE}
		echo "${L4T_ST_KEY_VER}=${L4T_VER}" >> ${L4T_ST_FILE}
		echo "${L4T_ST_KEY_BOARD}=${BOARD_NAME}" >> ${L4T_ST_FILE}
		echo "${L4T_ST_KEY_KERNEL_DTB}=BSP" >> ${L4T_ST_FILE}
		echo "${L4T_ST_KEY_STATUS}=NONE" >> ${L4T_ST_FILE}
		echo "${L4T_ST_KEY_OPT}=NONE" >> ${L4T_ST_FILE}
		echo "${L4T_ST_KEY_PREV}=NONE" >> ${L4T_ST_FILE}
	else
		if [[ ! -f ${L4T_ST_FILE} ]]; then
			echo -e "[ERROR] L4T status file does not exist, L4T current status lost:"
			echo -e ">>> ${L4T_ST_FILE}\n"
			exit 1
		fi

		case ${SUB_CMD} in
			"showall")
				for READ_LINE in $(cat ${L4T_ST_FILE}); do
					READ_KEY=$(echo ${READ_LINE} | awk -F '=' '{print $1}')
					READ_VALUE=$(echo ${READ_LINE} | awk -F '=' '{print $2}')
					echo -e "${READ_KEY}\t: ${READ_VALUE}"
				done
				;;
			"get")
				if [[ $# -lt 2 ]]; then
					echo -e "[ERROR] Function parameter error: ${FUNCNAME} $@\n"
					exit 1
				fi
				local KEY=$2
				for READ_LINE in $(cat ${L4T_ST_FILE}); do
					READ_KEY=$(echo ${READ_LINE} | awk -F '=' '{print $1}')
					READ_VALUE=$(echo ${READ_LINE} | awk -F '=' '{print $2}')

					if [[ ${READ_KEY} == ${KEY} ]]; then
						echo ${READ_VALUE}
						FLAG="FOUND"
					fi
				done
				if [[ ${FLAG} != "FOUND" ]]; then
					echo "NONE"
				fi
				;;
			"set")
				if [[ $# -lt 3 ]]; then
					echo -e "[ERROR] Function parameter error: ${FUNCNAME} $@\n"
					exit 1
				fi

				local KEY=$2
				local VALUE=$3
				for READ_LINE in $(cat ${L4T_ST_FILE}); do
					READ_KEY=$(echo ${READ_LINE} | awk -F '=' '{print $1}')
					READ_VALUE=$(echo ${READ_LINE} | awk -F '=' '{print $2}')

					if [[ ${READ_KEY} == ${KEY} ]]; then
						sed -i "s/${READ_KEY}=${READ_VALUE}/${READ_KEY}=${VALUE}/g" ${L4T_ST_FILE}
						FLAG="FOUND"
					fi
				done
				if [[ ${FLAG} != "FOUND" ]]; then
					echo "${KEY}=${VALUE}" >> ${L4T_ST_FILE}
				fi
				;;
		esac
	fi
}

function traverse()
{
	local dir=$1
	local func=$2
	shift 2
	for file in $(ls ${dir})
	do
		current=${dir}/${file}
		if [[ -d ${current} ]]; then
			${FUNCNAME} ${current} ${func} $@
		else
			${func} ${current} $@
		fi
	done
}

function traverse_func_patch_gen()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	local changed_file=$1
	local bsp_file=${L4T_TOP_TMP}/$(echo ${changed_file} | cut -d/ -f3-)
	local patch_file=${L4T_PATCH_TOP}/$(basename ${changed_file}).patch

	echo -e "[INFO] >>> BSP Chgd file: ${changed_file}"
	echo -e "[INFO] >>> BSP Orig file: ${bsp_file}"

	if [[ -f ${bsp_file} ]]; then
		echo -e "[INFO] Generating patch: ${patch_file}"
		diff -urN ${bsp_file} ${changed_file} > ${patch_file}
		echo -e "[INFO] Done\n"
	else
		local bsp_file_dts=${bsp_file}
		local bsp_file_dtb=${bsp_file:0:${#bsp_file}-1}b

		if [[ -f ${bsp_file_dtb} && ${bsp_file_dtb:0-4:4} == ".dtb" ]]; then
			echo -e "[INFO] Generating DTB file patch: ${patch_file}"
			dtc -I dtb -O dts ${bsp_file_dtb} -o ${bsp_file_dts} &> /dev/null
			diff -urN ${bsp_file_dts} ${changed_file} > ${patch_file}
			rm ${bsp_file_dts}
			echo -e "[INFO] Done\n"
		else
			echo -e "[ERROR] BSP Orig file does not exist: ${bsp_file}\n"
			exit 1
		fi
	fi
}

function traverse_func_patch_apply()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	local patch_file=$1
	local patch_file_abs=${PWD}/${patch_file}
	local bsp_file=$(head -n 1 ${patch_file} | awk '{print $2}')

	if [[ -f ${bsp_file} ]]; then
		echo -e "[INFO] Applying patch: ${patch_file}"
		sudo_cmd_check_w ${bsp_file} "patch -d ${L4T_TOP_TMP} -p 2 -i ${patch_file_abs}"
		echo -e "[INFO] Done\n"
	else
		local bsp_file_dts=${bsp_file}
		local bsp_file_dtb=${bsp_file_dts:0:${#bsp_file_dts}-1}b

		if [[ -f ${bsp_file_dtb} && ${bsp_file_dtb:0-4:4} == ".dtb" ]]; then
			echo -e "[INFO] Applying DTB patch: ${patch_file}"
			dtc -I dtb -O dts ${bsp_file_dtb} -o ${bsp_file_dts} &> /dev/null
			sudo_cmd_check_w ${bsp_file} "patch -d ${L4T_TOP_TMP} -p 2 -i ${patch_file_abs}"
			dtc -I dts -O dtb ${bsp_file_dts} -o ${bsp_file_dtb} &> /dev/null
			rm ${bsp_file_dts}
			echo -e "[INFO] Done\n"
		else
			echo -e "[ERROR] BSP Orig file which need patched does not exist: ${bsp_file}\n"
			exit 1
		fi
	fi
}

function traverse_func_patch_revert()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	local patch_file=$1
	local patch_file_abs=${PWD}/${patch_file}
	local bsp_file=$(head -n 1 ${patch_file} | awk '{print $2}')

	if [[ -f ${bsp_file} ]]; then
		echo -e "[INFO] Reverting patch: ${patch_file}"
		sudo_cmd_check_w ${bsp_file} "patch -d ${L4T_TOP_TMP} -p 2 -RE -i ${patch_file_abs}"
		echo -e "[INFO] Done\n"
	else
		local bsp_file_dts=${bsp_file}
		local bsp_file_dtb=${bsp_file_dts:0:${#bsp_file_dts}-1}b

		if [[ -f ${bsp_file_dtb} && ${bsp_file_dtb:0-4:4} == ".dtb" ]]; then
			echo -e "[INFO] Reverting DTB patch: ${patch_file}"
			dtc -I dtb -O dts ${bsp_file_dtb} -o ${bsp_file_dts} &> /dev/null
			sudo_cmd_check_w ${bsp_file} "patch -d ${L4T_TOP_TMP} -p 2 -RE -i ${patch_file_abs}"
			dtc -I dts -O dtb ${bsp_file_dts} -o ${bsp_file_dtb} &> /dev/null
			rm ${bsp_file_dts}
			echo -e "[INFO] Done\n"
		else
			echo -e "[ERROR] BSP Orig file which need reverted does not exist: ${bsp_file}\n"
			exit 1
		fi
	fi
}

function traverse_func_rootfs_tools_install()
{
	#echo -e "[DEBUG] ${FUNCNAME} $@"

	local filename=$1
	local filetype=$(file -b ${filename} | awk -F ',' '{print $1}')
	local rootfs_file=${L4T_TOP}/rootfs/$(echo ${filename} | cut -d/ -f2-)
	local rootfs_file_dir=$(dirname ${rootfs_file})

	if [[ ${filetype} =~ Bourne-Again ]] \
		|| [[ ${filetype} =~ ELF ]]; then

		echo -e ">>> ${rootfs_file} (+x)"
		sudo_cmd_check_w ${filename} "chmod a+x ${filename}"
	else
		echo -e ">>> ${rootfs_file}"
	fi

	if [[ ! -d ${rootfs_file_dir} ]]; then
		sudo_cmd "mkdir -p ${rootfs_file_dir}"
	fi
	sudo_cmd "cp ${filename} ${rootfs_file}"
}

function rootfs_tools_install()
{
	echo -e "[INFO] Add Ritrontek tools into Jetson L4T rootfs:"

	local TMPROOTFS=tmprootfs
	local TMPROOTFS_OPT_RK=${TMPROOTFS}/opt/rk
	local TMPROOTFS_ETC=${TMPROOTFS}/etc
	mkdir -p ${TMPROOTFS_OPT_RK} \
			${TMPROOTFS_ETC}

	case ${BOARD_NAME} in
		"RKNS02NX0" | "RKNS02N1")
			echo -e '#!/bin/bash\n' >  ${TMPROOTFS_ETC}/rc.local
			sed "s/_AUD_DEV_PLACEHOLDER_/jetsonxaviernxa/g" l4t_rootfs_tools/rc.local.nx.audio \
									>> ${TMPROOTFS_ETC}/rc.local
			sed "s/_AUD_DEV_PLACEHOLDER_/jetsonxaviernxa/g" l4t_rootfs_tools/jetson_aplay.sh \
							> ${TMPROOTFS_OPT_RK}/jetson_aplay.sh
			sed "s/_AUD_DEV_PLACEHOLDER_/jetsonxaviernxa/g" l4t_rootfs_tools/jetson_arecord.sh \
							> ${TMPROOTFS_OPT_RK}/jetson_arecord.sh
			cp l4t_rootfs_tools/aud_picker.sh.nx ${TMPROOTFS_OPT_RK}/aud_picker.sh
			cp l4t_rootfs_tools/song-youth.wav ${TMPROOTFS_OPT_RK}

			if [[ ${BOARD_NAME} == "RKNS02N1" ]]; then
				cp l4t_rootfs_tools/gpio_pwr12v_out.sh ${TMPROOTFS_OPT_RK}
				sed "s/RESOLUTION=1920x1080/RESOLUTION=1600x1300/g" l4t_rootfs_tools/jetson_camera.sh \
							> ${TMPROOTFS_OPT_RK}/jetson_camera.sh
			fi
			;;
		"RKNB10XA2")
			echo -e '#!/bin/bash\n' >  ${TMPROOTFS_ETC}/rc.local
			cat l4t_rootfs_tools/rc.local.camera \
									>> ${TMPROOTFS_ETC}/rc.local
			sed "s/_AUD_DEV_PLACEHOLDER_/tegrasndt19xmob/g" l4t_rootfs_tools/rc.local.xavier.audio \
									>> ${TMPROOTFS_ETC}/rc.local
			sed "s/_AUD_DEV_PLACEHOLDER_/tegrasndt19xmob/g" l4t_rootfs_tools/jetson_aplay.sh \
							> ${TMPROOTFS_OPT_RK}/jetson_aplay.sh
			sed "s/_AUD_DEV_PLACEHOLDER_/tegrasndt19xmob/g" l4t_rootfs_tools/jetson_arecord.sh \
							> ${TMPROOTFS_OPT_RK}/jetson_arecord.sh
			cp l4t_rootfs_tools/jetson_camera*.sh ${TMPROOTFS_OPT_RK}
			cp l4t_rootfs_tools/jetson_pwm.sh ${TMPROOTFS_OPT_RK}
			cp l4t_rootfs_tools/jetson_can.sh ${TMPROOTFS_OPT_RK}
			cp l4t_rootfs_tools/song-youth.wav ${TMPROOTFS_OPT_RK}
			;;
		"RKNS0AN0-32" | "RKNS0AN0-64")
			echo -e '#!/bin/bash\n' >  ${TMPROOTFS_ETC}/rc.local
			cat l4t_rootfs_tools/rc.local.camera \
									>> ${TMPROOTFS_ETC}/rc.local
			sed "s/_AUD_DEV_PLACEHOLDER_/APE/g" l4t_rootfs_tools/rc.local.orin.audio \
									>> ${TMPROOTFS_ETC}/rc.local
			sed "s/_AUD_DEV_PLACEHOLDER_/APE/g" l4t_rootfs_tools/jetson_aplay.sh \
							> ${TMPROOTFS_OPT_RK}/jetson_aplay.sh
			sed "s/_AUD_DEV_PLACEHOLDER_/APE/g" l4t_rootfs_tools/jetson_arecord.sh \
							> ${TMPROOTFS_OPT_RK}/jetson_arecord.sh
			cp l4t_rootfs_tools/jetson_camera.sh ${TMPROOTFS_OPT_RK}/jetson_camera.sh
			cp l4t_rootfs_tools/jetson_pwm.sh ${TMPROOTFS_OPT_RK}
			cp l4t_rootfs_tools/jetson_can.sh ${TMPROOTFS_OPT_RK}
			cp l4t_rootfs_tools/aud_picker.sh.orin ${TMPROOTFS_OPT_RK}/aud_picker.sh
			cp l4t_rootfs_tools/song-youth.wav ${TMPROOTFS_OPT_RK}

			# RK Sensors Demo
			local RK_SENSORS_SRC_URL="gitea@192.168.1.55:pan.kang/rk_sensors_RKNS0AN0.git"
			local RK_SENSORS_SRC_DIR="${HOME}/nvidia/source_code/rk_sensors_RKNS0AN0"
			if [[ ! -d ${RK_SENSORS_SRC_DIR} ]]; then
				git clone ${RK_SENSORS_SRC_URL} ${RK_SENSORS_SRC_DIR}
			fi
			if [[ ! -f ${RK_SENSORS_SRC_DIR}/rk_sensors ]]; then
				make -C ${RK_SENSORS_SRC_DIR} \
					CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}
			fi
			local TMPROOTFS_OPT_RK_SENSORS=${TMPROOTFS_OPT_RK}/rk_sensors
			mkdir -p ${TMPROOTFS_OPT_RK_SENSORS}
			cp ${RK_SENSORS_SRC_DIR}/rk_sensors ${TMPROOTFS_OPT_RK_SENSORS}
			cp ${RK_SENSORS_SRC_DIR}/README.md ${TMPROOTFS_OPT_RK_SENSORS}

			# Quectel
			local QUECTEL_SRC_URL="gitea@192.168.1.55:pan.kang/quectel_service.git"
			local QUECTEL_SRC_DIR="${HOME}/nvidia/source_code/quectel_service"
			if [[ ! -d ${QUECTEL_SRC_DIR} ]]; then
				git clone ${QUECTEL_SRC_URL} ${QUECTEL_SRC_DIR} --recursive
			fi
			sudo_cmd "cp ${QUECTEL_SRC_DIR}/quectel.service ${L4T_TOP}/rootfs/lib/systemd/system/"

			local QUECTEL_SRC_CM_DIR="${QUECTEL_SRC_DIR}/Quectel_QConnectManager_Linux_V1.6.4"
			if [[ ! -f ${QUECTEL_SRC_CM_DIR}/quectel-CM ]]; then
				make -C ${QUECTEL_SRC_CM_DIR} \
					CROSS_COMPILE=${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}
			fi

			local TMPROOTFS_OPT_RK_QUECTEL=${TMPROOTFS_OPT_RK}/quectel
			mkdir -p ${TMPROOTFS_OPT_RK_QUECTEL}
			cp ${QUECTEL_SRC_CM_DIR}/quectel-CM \
				${QUECTEL_SRC_DIR}/README.md \
				${QUECTEL_SRC_DIR}/Quectel_RG200U*.pdf \
				${QUECTEL_SRC_DIR}/Quectel_EC20*.pdf \
				${TMPROOTFS_OPT_RK_QUECTEL}

			# mcu
			if [[ ${BOARD_NAME} == "RKNS0AN0-32" ]]; then
				local TMPROOTFS_OPT_RK_MCU=${TMPROOTFS_OPT_RK}/mcu
				mkdir -p ${TMPROOTFS_OPT_RK_MCU}
				local SAMBA_MCU_DIR=/home/samba/samba_share/tmpshare/rt/707/candemo/
				sshpass -p ${PASSWD} scp rk@192.168.1.55:${SAMBA_MCU_DIR}/kkdemo_* \
							${TMPROOTFS_OPT_RK_MCU}/
			fi
			;;
		"RKPE32AN0")
			echo -e '#!/bin/bash\n' >  ${TMPROOTFS_ETC}/rc.local
			cat l4t_rootfs_tools/rc.local.camera \
									>> ${TMPROOTFS_ETC}/rc.local
			cp l4t_rootfs_tools/jetson_camera*.sh ${TMPROOTFS_OPT_RK}
			cp l4t_rootfs_tools/jetson_pwm.sh ${TMPROOTFS_OPT_RK}
			cp l4t_rootfs_tools/jetson_can.sh ${TMPROOTFS_OPT_RK}
			cp l4t_rootfs_tools/gpio_led_cidi.sh ${TMPROOTFS_OPT_RK}/gpio_led.sh

			# Neoway
			local NEOWAY_PPP_DIR=${HOME}/nvidia/source_code/neoway_n720_ppp
			if [[ ! -d ${NEOWAY_PPP_DIR} ]]; then
				git clone gitea@192.168.1.55:pan.kang/neoway_n720_ppp.git ${NEOWAY_PPP_DIR}
			fi
			sudo_cmd "cp ${NEOWAY_PPP_DIR}/neoway_ppp_config/* ${L4T_TOP}/rootfs/etc/ppp/peers/"
			sudo_cmd "cp ${NEOWAY_PPP_DIR}/neoway_ppp.service ${L4T_TOP}/rootfs/lib/systemd/system/"

			local TMPROOTFS_OPT_RK_NEOWAY=${TMPROOTFS_OPT_RK}/neoway
			mkdir -p ${TMPROOTFS_OPT_RK_NEOWAY}
			cp "${NEOWAY_PPP_DIR}/README_for_user.md" \
				"${TMPROOTFS_OPT_RK_NEOWAY}/Neoway_PPP_README.md"
			cp "${NEOWAY_PPP_DIR}/Neoway_N720_Linux_user_manual_V1.4.pdf" \
				${TMPROOTFS_OPT_RK_NEOWAY}

			# mcu
			local TMPROOTFS_OPT_RK_MCU=${TMPROOTFS_OPT_RK}/mcu
			mkdir -p ${TMPROOTFS_OPT_RK_MCU}
			local MCU_APP_VER=1.2
			local SAMBA_MCU_DIR=/home/samba/samba_share/tmpshare/rt/CIDI_MCU_Control
			sshpass -p ${PASSWD} scp rk@192.168.1.55:${SAMBA_MCU_DIR}/CIDI_MCU_Control_user_manual.pdf \
						${TMPROOTFS_OPT_RK_MCU}
			sshpass -p ${PASSWD} scp rk@192.168.1.55:${SAMBA_MCU_DIR}/CIDI_MCU_Control_V${MCU_APP_VER} \
						${TMPROOTFS_OPT_RK_MCU}/CIDI_MCU_Control
			;;
	esac

	traverse ${TMPROOTFS} traverse_func_rootfs_tools_install
	rm -rf ${TMPROOTFS}

	echo -e "[INFO] Done\n"
}

function rootfs_tools_uninstall()
{
	echo -e "[INFO] Remove Ritrontek tools from Jetson L4T rootfs:"

	local ROOTFS_OPT_RK=${L4T_TOP}/rootfs/opt/rk
	echo -e ">>> ${ROOTFS_OPT_RK}"
	sudo_cmd "rm -rf ${ROOTFS_OPT_RK}"

	local ROOTFS_ETC=${L4T_TOP}/rootfs/etc
	echo -e ">>> ${ROOTFS_ETC}/rc.local"
	sudo_cmd "rm -f ${ROOTFS_ETC}/rc.local"

	local ROOTFS_ETC_PPP=${L4T_TOP}/rootfs/etc/ppp/peers
	echo -e ">>> ${ROOTFS_ETC_PPP}/Neoway*"
	sudo_cmd "rm -f ${ROOTFS_ETC_PPP}/Neoway*"

	local ROOTFS_LIB_SYSTEMD=${L4T_TOP}/rootfs/lib/systemd/system
	echo -e ">>> ${ROOTFS_LIB_SYSTEMD}/neoway*"
	sudo_cmd "rm -f ${ROOTFS_LIB_SYSTEMD}/neoway*"
	echo -e ">>> ${ROOTFS_LIB_SYSTEMD}/quectel*"
	sudo_cmd "rm -f ${ROOTFS_LIB_SYSTEMD}/quectel*"

	echo -e "[INFO] Done\n"
}

function cmd_patch_gen()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	if [[ ! -d ${L4T_CHANGED_TOP} ]]; then
		echo -e "[ERROR] The L4T_CHANGED_TOP doesnot exist: ${L4T_CHANGED_TOP}\n"
		exit 1
	fi

	if [[ ! -d ${L4T_PATCH_TOP} ]]; then
		mkdir -p ${L4T_PATCH_TOP}
	fi
	if [[ -L ${L4T_TOP_TMP} ]]; then
		rm ${L4T_TOP_TMP}
	fi
	ln -s ${L4T_TOP} ${L4T_TOP_TMP}

	traverse ${L4T_CHANGED_TOP} traverse_func_patch_gen $@

	if [[ -L ${L4T_TOP_TMP} ]]; then
		rm ${L4T_TOP_TMP}
	fi
}

function cmd_patch_apply()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	if [[ $(l4t_st_op get ${L4T_ST_KEY_VER}) != ${L4T_VER} \
		|| $(l4t_st_op get ${L4T_ST_KEY_BOARD}) != ${BOARD_NAME} \
		|| $(l4t_st_op get ${L4T_ST_KEY_STATUS}) != "NONE" ]]; then
		echo -e "[ERROR] L4T current status cannot be apply patch, please confirm:"
		l4t_st_op showall
		exit 1
	fi

	if [[ -d ${L4T_PATCH_TOP} ]]; then
		if [[ -L ${L4T_TOP_TMP} ]]; then
			rm ${L4T_TOP_TMP}
		fi
		ln -s ${L4T_TOP} ${L4T_TOP_TMP}

		traverse ${L4T_PATCH_TOP} traverse_func_patch_apply $@

		if [[ -L ${L4T_TOP_TMP} ]]; then
			rm ${L4T_TOP_TMP}
		fi
		echo -e "[INFO] Apply all patches successfully:"
		echo -e ">>> ${L4T_PATCH_TOP}\n"
	else
		echo -e "[WARN] The L4T_PATCH_TOP doesnot exist: ${L4T_PATCH_TOP}\n"
	fi

	rootfs_tools_install

	l4t_st_op set ${L4T_ST_KEY_STATUS} "PATCHED"
	l4t_st_op set ${L4T_ST_KEY_OPT} $(basename ${L4T_PATCH_TOP})
}

function cmd_patch_revert()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	if [[ $(l4t_st_op get ${L4T_ST_KEY_VER}) != ${L4T_VER} \
		|| $(l4t_st_op get ${L4T_ST_KEY_BOARD}) != ${BOARD_NAME} \
		|| $(l4t_st_op get ${L4T_ST_KEY_STATUS}) != "PATCHED" ]]; then
		echo -e "[ERROR] L4T current status cannot revert patch, please confirm:"
		l4t_st_op showall
		exit 1
	fi

	if [[ -d ${L4T_PATCH_TOP} ]]; then
		if [[ -L ${L4T_TOP_TMP} ]]; then
			rm ${L4T_TOP_TMP}
		fi
		ln -s ${L4T_TOP} ${L4T_TOP_TMP}

		traverse ${L4T_PATCH_TOP} traverse_func_patch_revert $@

		if [[ -L ${L4T_TOP_TMP} ]]; then
			rm ${L4T_TOP_TMP}
		fi
		echo -e "[INFO] Revert all patches successfully:"
		echo -e ">>> ${L4T_PATCH_TOP}\n"
	else
		echo -e "[WARN] The L4T_PATCH_TOP doesnot exist: ${L4T_PATCH_TOP}\n"
	fi

	rootfs_tools_uninstall

	l4t_st_op set ${L4T_ST_KEY_STATUS} "NONE"
	l4t_st_op set ${L4T_ST_KEY_OPT} "NONE"
}

function cmd_switch_bsp()
{
	echo -e "[DEBUG] ${FUNCNAME}"

	local KERNEL=${L4T_TOP}/kernel/Image
	local KERNEL_BSP=${KERNEL}.bsp
	local DTB=${L4T_TOP}/kernel/dtb/${L4T_DTB_ARRAY[${BOARD_NAME}]}
	local DTB_BSP=${DTB}.bsp

	if [[ $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) == "SRC" ]]; then
		l4t_src_modules_uninstall
		l4t_src_apt_source_uninstall
	fi

	if [[ ! -f ${KERNEL_BSP} ]]; then
		mv ${KERNEL} ${KERNEL_BSP}
	fi
	ln -sf ${KERNEL_BSP} ${KERNEL}

	if [[ -f ${DTB} ]] && [[ ! -f ${DTB_BSP} ]]; then
		mv ${DTB} ${DTB_BSP}
	fi
	ln -sf ${DTB_BSP} ${DTB}

	local DTBO_BSP_LIST=$(ls $(dirname ${DTB})/*.dtbo.bsp 2>/dev/null)
	local DTBO_BSP=
	local DTBO=
	for DTBO_BSP in ${DTBO_BSP_LIST}
	do
		# DTBO_BSP = *.dtbo.bsp
		#     DTBO = *.dtbo
		DTBO=${DTBO_BSP:0:${#DTBO_BSP}-4}
		if [[ -f ${DTBO_BSP} ]]; then
			mv ${DTBO_BSP} ${DTBO}
		fi
	done

	L4T_VERMAGIC=$(strings ${L4T_TOP}/kernel/Image | grep -i "Linux version [0-9]" | awk '{print $3}')
	l4t_st_op set ${L4T_ST_KEY_KERNEL_DTB} "BSP"

	echo -e "\n[INFO] Switch to BSP Done"
	show_image_dtb_status
}

function cmd_switch_src()
{
	echo -e "[DEBUG] ${FUNCNAME}"
	echo -e "[INFO] Switching the Kernel and DTB with: L4T SRC Output"

	local KERNEL=${L4T_TOP}/kernel/Image
	local KERNEL_BSP=${KERNEL}.bsp
	local KERNEL_SRC=${L4T_SRC_OUTPUT_TOP}/arch/arm64/boot/Image
	local DTB=${L4T_TOP}/kernel/dtb/${L4T_DTB_ARRAY[${BOARD_NAME}]}
	local DTB_BSP=${DTB}.bsp
	if [[ ${L4T_VER_MAJOR} -le 32 ]]; then
		local DTB_SRC=${L4T_SRC_OUTPUT_TOP}/arch/arm64/boot/dts/${L4T_DTB_ARRAY[${BOARD_NAME}]}
	else
		local DTB_SRC=${L4T_SRC_OUTPUT_TOP}/arch/arm64/boot/dts/nvidia/${L4T_DTB_ARRAY[${BOARD_NAME}]}
	fi

	# Check the src output file whether exist
	if [[ ! -f ${KERNEL_SRC} ]]; then
		echo -e "[ERROR] L4T SRC Kernel Image doesnot exist:"
		echo -e ">>> ${KERNEL_SRC}\n"
		exit 1
	fi
	if [[ ! -f ${DTB_SRC} ]]; then
		echo -e "[ERROR] L4T SRC DTB doesnot exist:"
		echo -e ">>> ${DTB_SRC}\n"
		exit 1
	fi

	if [[ ! -f ${KERNEL_BSP} ]]; then
		mv ${KERNEL} ${KERNEL_BSP}
	fi
	ln -sf ${KERNEL_SRC} ${KERNEL}

	if [[ -f ${DTB} ]] && [[ ! -f ${DTB_BSP} ]]; then
		mv ${DTB} ${DTB_BSP}
	fi
	ln -sf ${DTB_SRC} ${DTB}

	local DTBO_SRC_LIST=$(ls $(dirname ${DTB_SRC})/*.dtbo 2>/dev/null)
	local DTBO_SRC=
	local DTBO_BSP=
	local DTBO=
	for DTBO_SRC in ${DTBO_SRC_LIST}
	do
		DTBO=$(dirname ${DTB})/$(basename ${DTBO_SRC})
		DTBO_BSP=${DTBO}.bsp
		if [[ -f ${DTBO} ]] && [[ ! -L ${DTBO} ]]; then
			mv ${DTBO} ${DTBO_BSP}
		fi
		ln -sf ${DTBO_SRC} ${DTBO}
	done

	L4T_VERMAGIC=$(strings ${L4T_TOP}/kernel/Image | grep -i "Linux version [0-9]" | awk '{print $3}')
	l4t_st_op set ${L4T_ST_KEY_KERNEL_DTB} "SRC"

	l4t_src_modules_install
	l4t_src_apt_source_install

	echo -e "\n[INFO] Switch to SRC Done"
	show_image_dtb_status
}

function cmd_bsp()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	if [[ $# -le 0 ]]; then
		echo -e "[ERROR] Must specify a BSP SUB CMD.\n"
		usage
		exit 1
	fi

	local SUB_CMD=$1
	shift 1

	case ${SUB_CMD} in
		"${CMD_BSP_ST}" )
			cmd_bsp_st
			;;
		"${CMD_BSP_RELEASE}" )
			cmd_bsp_release
			;;
		"${CMD_BSP_INSTALL}" )
			cmd_bsp_install $@
			;;
		"${CMD_BSP_UNINSTALL}" )
			cmd_bsp_uninstall
			;;
		"${CMD_BSP_BACKUP}" )
			cmd_bsp_backup
			;;
		"${CMD_BSP_RESTORE}" )
			cmd_bsp_restore $@
			;;
		* )
			echo -e "[ERROR] Unsupport BSP CMD: ${SUB_CMD}\n"
			usage
			exit 1
			;;
	esac
}

function cmd_src()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	if [[ $# -le 0 ]]; then
		echo -e "[ERROR] Must specify a SRC SUB CMD.\n"
		usage
		exit 1
	fi

	if [[ ! -f ${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}gcc ]]; then
		echo -e "[ERROR] L4T toolchain does not exist, please download and extract as below:"
		echo -e ">>> ${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}\n"
		exit 1
	fi
	echo -e "[INFO] Toolchain: ${L4T_TOOLCHAIN_ARRAY[${L4T_VER}]}"

	local board=
	local FLAG=
	for board in ${!L4T_SRC_BRANCH_ARRAY[@]}; do
		if [[ ${board} == ${BOARD_NAME} ]]; then
			FLAG="FOUND"
		fi
	done
	if [[ ${FLAG} != "FOUND" ]]; then
		echo -e "[ERROR] There is no SRC Branch for this Board.\n"
		exit 1
	fi

	l4t_src_init

	local SUB_CMD=$1
	shift 1

	case ${SUB_CMD} in
		"${CMD_SRC_BUILD}" )
			cmd_src_build $@
			;;
		"${CMD_SRC_INSTALL}" )
			cmd_src_install
			;;
		* )
			echo -e "[ERROR] Unsupport SRC CMD: ${SUB_CMD}\n"
			usage
			exit 1
			;;
	esac
}

function cmd_patch()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	if [[ $# -le 0 ]]; then
		echo -e "[ERROR] Must specify a PATCH SUB CMD.\n"
		usage
		exit 1
	fi

	if [[ $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) == "PKG" \
		|| $(l4t_st_op get ${L4T_ST_KEY_STATUS}) == "INSTALLED" ]]; then

		echo -e "[ERROR] Error status, release package installed, cannot perform patch function."
		l4t_st_op showall
		exit 1
	fi

	local SUB_CMD=$1
	shift 1

	case ${SUB_CMD} in
		"${CMD_PATCH_GEN}" )
			cmd_patch_gen
			;;
		"${CMD_PATCH_APPLY}" )
			cmd_patch_apply
			;;
		"${CMD_PATCH_REVERT}" )
			cmd_patch_revert
			;;
		* )
			echo -e "[ERROR] Unsupport PATCH CMD: ${SUB_CMD}\n"
			usage
			exit 1
			;;
	esac
}

function cmd_switch()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	if [[ $# -le 0 ]]; then
		echo -e "[ERROR] Must specify a SWITCH SUB CMD.\n"
		usage
		exit 1
	fi

	if [[ $(l4t_st_op get ${L4T_ST_KEY_KERNEL_DTB}) == "PKG" \
		|| $(l4t_st_op get ${L4T_ST_KEY_STATUS}) == "INSTALLED" ]]; then

		echo -e "[ERROR] Error status, release package installed, cannot perform switch function."
		l4t_st_op showall
		exit 1
	fi

	local SUB_CMD=$1
	shift 1

	case ${SUB_CMD} in
		"${CMD_SWITCH_BSP}" )
			cmd_switch_bsp
			;;
		"${CMD_SWITCH_SRC}" )
			cmd_switch_src
			;;
		* )
			echo -e "[ERROR] Unsupport SWITCH CMD: ${SUB_CMD}\n"
			usage
			exit 1
			;;
	esac
}

function cmd_flash()
{
	echo -e "[DEBUG] ${FUNCNAME} $@"

	# Default to flash all partition
	local FLASH_OPTIONS=""
	local FLASH_DEV_PARTITION="mmcblk0p1"

	if [[ $# -ge 1 ]]; then
		local SUB_CMD=$1
		shift
		FLASH_OPTIONS=$@

		case ${SUB_CMD} in
			"${CMD_FLASH_EMMC}" )
				FLASH_DEV_PARTITION="mmcblk0p1"
				;;
			"${CMD_FLASH_NVME}" )
				FLASH_DEV_PARTITION="nvme0n1p1"
				;;
			* )
				echo -e "[ERROR] Unsupport FLASH DEVICE: ${SUB_CMD}\n"
				usage
				exit 1
				;;
		esac
	fi

	local FLASH_CMD="./flash.sh ${FLASH_OPTIONS} ${L4T_CONF_ARRAY[${BOARD_NAME}]} ${FLASH_DEV_PARTITION}"
	local board_usb=$(lsusb -d 0x0955:)
	if [[ -z ${board_usb} ]]; then
		echo -e "[INFO] Please connect the Jetson Device to HOST, and put it into recovery mode."
		read -p "Press any key to continue ..." key

		local board_usb=$(lsusb -d 0x0955:)
		if [[ -z ${board_usb} ]]; then
			echo -e "[ERROR] Jetson Board does not connect, cannot flash, exit!"
			exit 1
		fi
	fi

	echo -e "[INFO] Flash command: ${FLASH_CMD}"
	read -p "Continue to flash? (Y/n):" key
	if [[ ${key} == "n" || ${key} == "N" ]]; then
		echo -e "[INFO] Exit"
		exit 0
	fi

	cd ${L4T_TOP}
	sudo_cmd ${FLASH_CMD}
	cd -
}

###################################################################################

if [[ $# -lt 3 ]]; then
	usage
	exit 1
fi

if [[ $(id -u) -eq 0 ]]; then
	echo -e "[ERROR] Cannot run as root!\n"
	exit 1
fi

init_host_env

init_global_var $1 $2

l4t_bsp_init

CMD=$3
shift 3
case ${CMD} in
	${CMD_BSP} )
		cmd_bsp $@
		;;
	${CMD_SRC} )
		cmd_src $@
		;;
	${CMD_PATCH} )
		cmd_patch $@
		;;
	${CMD_SWITCH} )
		cmd_switch $@
		;;
	${CMD_FLASH} )
		cmd_flash $@
		;;
	* )
		echo -e "[ERROR] Unsupport CMD: ${CMD}\n"
		usage
		exit 1
		;;
esac
